BBN-Vax-TCP/bbnnet-oct82/netuser.c

Compare this file to the similar file:
Show the results in this format:

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/buf.h"
#include "../h/dir.h"
#include "../h/file.h"
#include "../h/inode.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/net.h"
#include "../bbnnet/imp.h"
#include "../bbnnet/ifcb.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/tcp.h"
#include "../bbnnet/raw.h"
#include "../bbnnet/udp.h"
#include "../bbnnet/ucb.h"
#include "../bbnnet/fsm.h"

/*
 * Network user i/f routines
 */

/*
 * Open a network connection.  Allocate an open file structure and a network
 * user control block (ucb).  Verify user open parameters and do protocol
 * specific open functions.
 */
netopen(ip, mode)
struct inode *ip;
char *mode;       
{
	register struct ucb *up;
	register struct con *cp;
	register struct file *fp;
	register struct ifcb *lp;
	register struct mbuf *m;
	int snd, rcv, mask, i;
	struct con const;
	net_t net;

	iput(ip);               /* release dev inode since we don't use it */

	/* get user connection structure parm */

	if (copyin(mode, &const, sizeof(const)) < 0) {
		u.u_error = EFAULT;
		return;
	}
	cp = (struct con *)&const;

	/* calculate send and receive buffer allocations */

	snd = cp->c_sbufs << 3;
	rcv = cp->c_rbufs << 3;
	if (snd < 8 || snd > 64)
		snd = 8;
	if (rcv < 8 || rcv > 64)
		rcv = 8;

	/* make sure system has enough buffers for connection */

	if (!(cp->c_mode&CONCTL) && 
	    (snd + rcv + 2) > (NNETPAGES*NMBPG-32-netcb.n_lowat)) {
		u.u_error = ENETBUF;
		return;
	}

	/* allocate file entry and ucb */

	if ((fp = falloc()) == NULL) 
		return;
	i = u.u_r.r_val1;

	if ((m = m_get(1)) == NULL) {
		u.u_error = ENETBUF;
		u.u_ofile[i] = NULL;
		return;
	}
	m->m_off = MHEAD;
	up = mtod(m, struct ucb *);
	up->uc_next = netcb.n_ucb_hd;
	up->uc_prev = NULL;
	if (netcb.n_ucb_hd != NULL)
		netcb.n_ucb_hd->uc_prev = up;
	netcb.n_ucb_hd = up;

	/* get user file table entry and point at ucb */

#ifndef mbb
	fp->f_flag = (FNET|FREAD|FWRITE);
#else
	fp->f_flag = (FTNET|FREAD|FWRITE);
#endif
	fp->f_un.f_ucb = up;
	fp->f_inode = NULL;

	/* init common ucb fields */

	up->uc_proc = u.u_procp;
	up->uc_proto = NULL;
	up->uc_snd = snd;
	up->uc_rcv = rcv;
	up->uc_sbuf = NULL;
	up->uc_rbuf = NULL;
	up->uc_timeo = cp->c_timeo;             /* overlays uc_ssize */
	up->uc_rsize = 0;
	up->uc_state = 0;
	up->uc_xstat = 0;
	up->uc_flags = cp->c_mode & ~(UTCP+ULOCK);

	/* control connections are all done here */

	if (up->uc_flags&UCTL)
		return;

	up->uc_flags ↑= CONACT;
	netcb.n_lowat += snd + rcv + 2;
	netcb.n_hiwat = (netcb.n_lowat * 3) >> 1;

	/* save foreign host address, route will be filled in later */

	up->uc_route = NULL;
	up->uc_host = cp->c_fcon;

	/* if local address is specified, make sure its one of ours and use
	   it if it is, otherwise pick the "best" local address from the
	   local i/f table */

	if (cp->c_lcon.s_addr != NULL) {
		up->uc_local = cp->c_lcon;
		cp->c_lcon.s_addr = addr_1822(cp->c_lcon);	/* GROT! */
		for (lp = netcb.n_ifcb_hd; lp != NULL; lp = lp->if_next) 
			if (lp->if_addr.s_addr == cp->c_lcon.s_addr)
				goto gotaddr;
		u.u_error = ENETRNG;	/* spec local address not in table */
		goto conerr;	
gotaddr:
		/* make sure specified i/f is available */

		if (lp->if_avail)
			up->uc_srcif = lp;
		else {
			u.u_error = ENETDOWN;
			goto conerr;
		}
	} else {

		/* find default 'preferred' address, look for first 
		   available i/f */

		up->uc_srcif = NULL;
		for (lp = netcb.n_ifcb_hd; lp != NULL; lp = lp->if_next) {
			if (lp->if_avail) {
				up->uc_srcif = lp;	
				net = iptonet(cp->c_fcon);
				break;
			}
		}

		/* error if none available */

		if (up->uc_srcif == NULL) {
			u.u_error = ENETDOWN;
			goto conerr;
		}

		/* if foreign host is on a net that we are, use the address
		   of the i/f on that net */

		for (lp = netcb.n_ifcb_hd; lp != NULL; lp = lp->if_next) 
			if (iptonet(lp->if_addr)==net && lp->if_avail) {
				up->uc_srcif = lp;
				break;
			}

		/* use default local addr for active opens, wild card for
		   passive. */

		if (cp->c_mode & CONACT)
			up->uc_local = up->uc_srcif->if_addr;
		else
			up->uc_local.s_addr = NULL;
	}

	/* call protocol open routine for rest of init */

	switch (cp->c_mode & CONMASK) {

	case CONTCP:
		tcpopen(up, cp);
		if (u.u_error)
			goto conerr;
		break;

	case CONUDP:
		up->uc_udp.u_fport = cp->c_fport;
		up->uc_udp.u_lport = uniqport(up, cp->c_lport, cp->c_fport);
		if (u.u_error)
			goto conerr;
		break;

	case CONRAW:
	case CONIP:
		raw_add(up, cp->c_proto);
		if (u.u_error)
			goto conerr;
		break;

       	default:
		u.u_error = ENETPARM;
	conerr:
		netclose(fp);
		u.u_ofile[i] = NULL;
	}
}

/*
 * TCP specific open routine.  Allocate a TCB and set up an open work request
 * for the finite state machine.
 */
tcpopen(up, cp)
register struct ucb *up;
register struct con *cp;
{
	register struct mbuf *m;
	register struct tcb *tp;
	register char *p;
	register i;

	/* allocate a buffer for a tcb */

	if ((m = m_get(1)) == NULL) {           /* no buffers */
		u.u_error = ENETBUF;    
		return;
	}
	m->m_off = MHEAD;
	tp = mtod(m, struct tcb *);		/* -> tcb */

	/* zero tcb for initialization */

	for (p = (char *)tp, i = MLEN; i > 0; i--)
		*p++ = 0;

	/* fill in ucb and tcb fields */

	tp->t_ucb = up;
	up->uc_tcb = tp;
	up->uc_flags |= UTCP;
	tp->t_fport = cp->c_fport;
	tp->t_lport = uniqport(up, cp->c_lport, cp->c_fport);

	/* if local port not unique, free tcb */

	if (u.u_error) {
		up->uc_flags &= ~UTCP;
		up->uc_tcb = NULL;
		m_free(m);
		return;
	}

	/*  make a user open work request for tcp */

	w_alloc((cp->c_mode & CONACT ? IUOPENR : IUOPENA), 0, tp, NULL);

	/* block until connection has opened or error */
	
	if (!(up->uc_flags & UNOBLOK))
		awaitconn(up, tp);
}

/*
 * Network close routine.  For TCP connections, issue a close work request to
 * the tcp fsm.  Otherwise, just clean up the ucb and the file structure 
 */
netclose(fp)
register struct file *fp; 
{
	register struct ucb *up;
	register struct tcb *tp;

	up = fp->f_un.f_ucb;

	if (up->uc_flags & UTCP) {      /* tcp connection */

		tp = up->uc_tcb;

		/* if connection has not already been closed, by user or
		   system, enqueue work request.  otherwise, if the tcb
		   has been deleted (completed close), then release the ucb */

		if (tp != NULL) {	/* tcb still exists */
			tp->usr_abort = TRUE;	/* says we've closed */
			if (!tp->usr_closed)
				w_alloc(IUCLOSE, 0, tp, up);
			if (up->uc_flags & UCWAIT)
				sleep(tp, PZERO+1);
			goto ufree;
		} 

	} else if (up->uc_flags&(UIP+URAW+UUDP)) {        /* raw connection */

	/* free ucb and lower system buffer allocation */

        	netcb.n_lowat -= up->uc_snd + up->uc_rcv + 2;
		netcb.n_hiwat = (netcb.n_lowat * 3) >> 1;
		if (up->uc_route != NULL) {
        		h_free(up->uc_route);
			up->uc_route = NULL;
		}
		if (!(up->uc_flags & UUDP))
			raw_free(up);
	}
	up->uc_proc = NULL;
	if (up->uc_next != NULL)
		up->uc_next->uc_prev = up->uc_prev;
	if (up->uc_prev != NULL)
		up->uc_prev->uc_next = up->uc_next;
	else
		netcb.n_ucb_hd = up->uc_next;
	m_free(dtom(up));
ufree:
	fp->f_flag = 0;
	fp->f_count = 0;
	fp->f_un.f_ucb = NULL;
}  

/*
 * Network read routine. Just call protocol specific read routine.
 */
netread(up)
register struct ucb *up;
{

	switch (up->uc_flags & CONMASK) {

	case UTCP:              
		tcp_read(up);
		break;
	case UIP:                                           
	case URAW:
	case UUDP:
		raw_read(up);
		break;
	default:
		u.u_error = ENETPARM;
	}
}

/*
 * Network write routine. Just call protocol specific write routine.
 */
netwrite(up)
register struct ucb *up;
{
	if (up->uc_state)
		u.u_error = ENETSTAT;		/* new status to report */
	else {
		switch (up->uc_flags & CONMASK) {

		case UTCP:              
			tcp_write(up);
			break;
		case UIP:                                           
		case URAW:
		case UUDP:
			raw_write(up);
			break;
		default:
			u.u_error = ENETPARM;
		}
	}
}       

/*
 * TCP read routine.  Copy data from the receive queue to user space.
 */
tcp_read(up)                     
register struct ucb *up;
{
	register struct mbuf *m;
	register struct tcb *tp;
	register len, eol;
	int incount;

	if ((tp = up->uc_tcb) == NULL) {
		u.u_error = ENETERR;
		return;
	}
	if (!tp->syn_acked)		/* block if not connected */
		awaitconn(up, tp);
	eol = FALSE;
	incount = u.u_count;

	while (u.u_count > 0 && !eol && !u.u_error) 

	        if (up->uc_rbuf != NULL) {	/* data available */

			/* lock recieve buffer */

                        while (up->uc_flags & ULOCK)
                        	sleep(&up->uc_flags, PZERO);
                        up->uc_flags |= ULOCK;
                        
			/* copy the data an mbuf at a time */

			m = up->uc_rbuf;
                        while (m != NULL && u.u_count > 0 && !eol && !u.u_error) {
                        
                        	len = MIN(m->m_len, u.u_count);
                        	iomove((caddr_t)((int)m + m->m_off), len, B_READ);
                        
                        	if (!u.u_error) {

					/* free or adjust mbuf */

                        		if (len == m->m_len) {
        				        eol = (int)m->m_act;
                        			m = m_free(m);
                        			--up->uc_rsize;
                        		} else {
                        			m->m_off += len;
                        			m->m_len -= len;
                        		}
                        	}
                        }
			up->uc_rbuf = m;
			up->uc_flags &= ~ULOCK; /* unlock buffer */
			wakeup(&up->uc_flags);
			/*
			 * If any data was read, return even if the
			 * full read request was not satisfied
			 */
			if (incount != u.u_count)
				return;
		} else if (up->uc_state == 0) {                
			/*
			 * If no data left and foreign FIN rcvd, just
			 * return, otherwise make work request and wait
			 * for more data 
			 */
			if (tp->fin_rcvd && rcv_empty(tp))       
				break;

        		w_alloc(IURECV, 0, tp, NULL);
			sleep(up, PZERO + 1);
		} else {
			/*
			 * Net status to return.
			 */
			u.u_error = ENETSTAT;
			break;
		}
}

/*
 * TCP write routine.  Copy data from user space to a net buffer chain.  Put
 * this chain on a send work request for the tcp fsm.
 */
tcp_write(up)                    
register struct ucb *up;
{
	register struct tcb *tp;
	register struct mbuf *m, *n;
	register len, bufs;
	int incount;
	struct {
		struct mbuf *top;
	} nn;

	if ((tp = up->uc_tcb) == NULL) {                    
		u.u_error = ENETERR;
		return;
	}
	if (!tp->syn_acked)		/* block if not connected */
		awaitconn(up, tp);
	incount = u.u_count;

	while (u.u_count > 0 && !u.u_error && up->uc_state == 0) {

		/* #bufs can send now */

		bufs = (int)up->uc_snd - (int)up->uc_ssize;       

		/* top of chain to send */

		nn.top = NULL;                          
		n = (struct mbuf *)&nn;

		/* copy data from user to mbuf chain for send work req */

        	while (bufs-- > 0 && u.u_count > 0) {
        
			/* set up an mbuf */

        		if ((m = m_get(1)) == NULL) {
				u.u_error = ENETBUF;
				return;
			}
        		m->m_off = MHEAD;

			/* copy user data in */

        		len = MIN(MLEN, u.u_count);
        		iomove((caddr_t)((int)m + m->m_off), len, B_WRITE);

			/* chain to work request mbufs or clean up if error */

        		if (!u.u_error) {
        			m->m_len = len;
        			n->m_next = m;
        			n = m;
        		} else {
        			m_freem(nn.top);
				return;
			}
        	}
        
        	w_alloc(IUSEND, 0, tp, nn.top);         /* make work req */

		/* if more to send, sleep on more buffers */

		if (u.u_count > 0)
			sleep(up, PZERO+1);
	}
}

/*
 * Raw IP and local net read routine.  Copy data from raw read queue to user
 * space.
 */
raw_read(up)                     
register struct ucb *up;
{
	register struct mbuf *m, *next;
	register len;
	int incount;

	incount = u.u_count;
	while (up->uc_rbuf == NULL && up->uc_state == 0)
		sleep(up, PZERO+1);

	/* lock recieve buffer */

        while (up->uc_flags & ULOCK)
        	sleep(&up->uc_flags, PZERO);
        up->uc_flags |= ULOCK;
        
	/* copy the data an mbuf at a time */

        if ((m = up->uc_rbuf) != NULL)
		next = m->m_act;
	else
		next = NULL;

        while (m != NULL && u.u_count > 0 && !u.u_error) {
        
        	len = MIN(m->m_len, u.u_count);
        	iomove((caddr_t)((int)m + m->m_off), len, B_READ);
        
        	if (!u.u_error) {

			/* free or adjust mbuf */

        		if (len == m->m_len) {
        			m = m_free(m);
        			--up->uc_rsize;
        		} else {
        			m->m_off += len;
        			m->m_len -= len;
        		}
        	}
        }

	if (m == NULL)
		up->uc_rbuf = next;
	else {
		m->m_act = next;
		up->uc_rbuf = m;
	}
	up->uc_flags &= ~ULOCK; /* unlock buffer */
	wakeup(&up->uc_flags);

	if (incount == u.u_count && up->uc_state != 0)
		u.u_error = ENETSTAT;
}

/*
 * Raw IP or local net write routine.  Copy data from user space to a network
 * buffer chain.  Call the appropriate format routine to set up a packet
 * header and send the data.
 */
raw_write(up)                    
register struct ucb *up;
{
	register struct mbuf *m, *n, *top;
	register len, bufs, ulen;

	/* get header mbuf */

	if ((top = n = m_get(1)) == NULL) {
		u.u_error = ENETBUF;
		return;
	}

	bufs = up->uc_snd;                      /* #bufs can send now */
	ulen = u.u_count;

	/* copy data from user to mbuf chain for send */

        while (bufs-- > 0 && u.u_count > 0) {
        
		/* set up an mbuf */

        	if ((m = m_get(1)) == NULL) {
			u.u_error = ENETBUF;
			return;
		}
        	m->m_off = MHEAD;
			
		/* copy user data in */

        	len = MIN(MLEN, u.u_count);
        	iomove((caddr_t)((int)m + m->m_off), len, B_WRITE);

		/* chain to header mbuf or clean up if error */

        	if (!u.u_error) {
        		m->m_len = len;
        		n->m_next = m;
        		n = m;
        	} else {
        		m_freem(top);
			return;
		}
        }

	/* only send raw message if all of it could be read */

	if (u.u_count > 0) {
		u.u_error = ERAWBAD;
		m_freem(top);
		return;
	}

	/* send to net */

	switch (up->uc_flags & CONMASK) {

	case UUDP:
		udp_output(up, top, ulen);
		break;

	case UIP:
        	ip_raw(up, top, ulen);
		break;

	case URAW:
		(*up->uc_srcif->if_raw)(up, top, ulen);
		break;

	default:
		m_freem(top);
		u.u_error = ENETPARM;
	}
}

/*
 * Network control functions
 */
netioctl(up, cmd, cmdp)
register struct ucb *up;
int cmd;
caddr_t cmdp;
{
	register struct inode *ip;
	register struct tcb *tp;
	register struct mbuf *m;
	register struct gway *gp;
	register struct ifcb *lp;
	char *p, *q;
	struct host *route;
	short proto;
	int len;
	struct socket addr;
	struct netstate nstat;
	struct netopt opt;
	struct netinit init;

	tp = up->uc_tcb;

	switch (cmd) {

	case NETGETS:                   /* get net status */
		bcopy((caddr_t)&up->uc_snd, (caddr_t)&nstat, sizeof(struct netstate));
		if (up->uc_flags & UTCP && tp != NULL) {
			nstat.n_lport = tp->t_lport;
			nstat.n_fport = tp->t_fport;
		} else if (up->uc_flags & UUDP) {
			nstat.n_lport = up->uc_udp.u_lport;
			nstat.n_fport = up->uc_udp.u_fport;
		}

		nstat.n_fcon = up->uc_host;
		nstat.n_lcon = up->uc_local;

		if (copyout((caddr_t)&nstat, cmdp, sizeof(struct netstate)))
			u.u_error = EFAULT;
		else {
			up->uc_xstat = 0;
			up->uc_state = 0;
		}
		break;

	case NETSETU:                   /* set urgent mode */
		up->uc_flags |= UURG;
		break;

	case NETRSETU:                  /* reset urgent mode */
		up->uc_flags &= ~UURG;
		break;

	case NETSETE:                   /* set eol mode */
		up->uc_flags |= UEOL;
		break;

	case NETRSETE:                  /* reset eol mode */
		up->uc_flags &= ~UEOL;
		break;

	case NETCLOSE:                  /* tcp close but continue to rcv */
		if ((up->uc_flags & UTCP) && tp != NULL && !tp->usr_closed) 
			w_alloc(IUCLOSE, 0, tp, up);
		break;

	case NETABORT:                  /* tcp user abort */
		if ((up->uc_flags & UTCP) && tp != NULL)
			w_alloc(IUABORT, 0, tp, up);
		break;

	case NETOWAIT:			/* tcp wait until connected */
		if (up->uc_flags&UTCP && tp != NULL && !tp->syn_acked)
			awaitconn(up, tp);
		break;

	case NETSETD:                   /* set debugging file */
		if (!suser()) {
			u.u_error = EPERM;
			break;
		}
		if (cmdp == NULL) {
			if (netcb.n_debug) {
				plock(netcb.n_debug);
				iput(netcb.n_debug);
				netcb.n_debug = NULL;
			}
		} else {
			if (netcb.n_debug != NULL) {
				u.u_error = EBUSY;
				break;
			}
			u.u_dirp = cmdp;
			if ((ip = namei(uchar, 0)) != NULL) {
				if ((ip->i_mode & IFMT) != IFREG) {
					u.u_error = EACCES;
					iput(ip);
				} else {
					netcb.n_debug = ip;
					prele(ip);
				}
			}
		}
		break;

	case NETRESET:                  /* forced tcp reset */
		if (!suser()) {
			u.u_error = EPERM;
			break;
		}
		for (up = netcb.n_ucb_hd; up != NULL; up = up->uc_next)
			if ((up->uc_flags & UTCP) && 
			     up->uc_tcb != NULL &&
			     up->uc_tcb == (struct tcb *)cmdp) {
				w_alloc(INCLEAR, 0, cmdp, NULL);
				return;
			}
		u.u_error = EINVAL;
		break;

	case NETDEBUG:                  /* toggle debugging flag */
		for (up = netcb.n_ucb_hd; up != NULL; up = up->uc_next) 
			if ((up->uc_flags & UTCP) && up->uc_tcb != NULL &&
			    up->uc_tcb == (struct tcb *)cmdp) {
				up->uc_flags ↑= UDEBUG;
				return;
			}
		u.u_error = EINVAL;
		break;
	
	case NETTCPOPT:			/* copy ip options to tcb */
		if ((up->uc_flags & UTCP) && tp != NULL && cmdp != NULL) {
			
			if (copyin((caddr_t)cmdp, &opt, sizeof opt)) {
				u.u_error = EFAULT;
				break;
			}

			/* maximum option string is 40 bytes */

			if (opt.nio_len > IPMAXOPT) {
				u.u_error = EINVAL;
				break;
			}

			/* get a buffer to hold the option string */

			if ((m = m_get(1)) == NULL) {
				u.u_error = ENETBUF;
				break;
			}
			m->m_off = MHEAD;
			m->m_len = opt.nio_len;

			if (copyin((caddr_t)opt.nio_opt, 
				(caddr_t)((int)m + m->m_off), opt.nio_len)) {
				m_free(m);
				u.u_error = EFAULT;
				break;	
			}
			tp->t_opts = mtod(m, char *);
			tp->t_optlen = opt.nio_len;
		}
		break;

	case NETGINIT:			/* reread the gateway file */
		if (!suser()) {
			u.u_error = EPERM;
			break;
		}
		if (cmdp == NULL)	/* reset the table */
			gatewayNGATE = gateway;
		else {
			gp = gatewayNGATE;
			/*
			 * too many entries
			 */
			if (ngate <= gp - gateway) {
				u.u_error = ETABLE;
				break;
			}
			if (copyin((caddr_t)cmdp, (caddr_t)gp, 
				   sizeof(struct gway))) {
				u.u_error = EFAULT;
				break;
			}
			/*
			 * can we use this entry?
			 */
			if (gp->g_flags&GWFORMAT) {
				for (lp=netcb.n_ifcb_hd; 
						  lp != NULL; lp=lp->if_next) 
					if (iptonet(lp->if_addr) == 
					    gp->g_lnet) {
						gp->g_ifcb = lp;
						gatewayNGATE = ++gp;
						return;
					}
			}
			u.u_error = EINVAL;
		}
		break;

	case NETPRADD:			/* add protocol numbers to ucb chain */
	case NETPRDEL:			/* delete protocol numbers from chain */
		/* get arg pointer and arg length and verify */

		if (copyin((caddr_t)cmdp, &opt, sizeof opt)) {
			u.u_error = EFAULT;
			break;
		}

		/* add or delete each proto number on ucb chain */

		for (len=opt.nio_len; len > 0; len -= sizeof(short)) {

			/* get next proto number from user */

			if (copyin((caddr_t)opt.nio_opt, proto, sizeof(short))){
				u.u_error = EFAULT;
				break;
			}
			if (cmd == NETPRADD)	
				raw_add(up, proto);
			else
				raw_del(up, proto);
			opt.nio_opt += sizeof(short);
		}
		break;

	case NETPRSTAT:			/* return current list of proto nos. */
		/* get arg pointer and arg length */

		if (copyin((caddr_t)cmdp, &opt, sizeof opt)) {
			u.u_error = EFAULT;
			break;
		}
		raw_stat(up, opt.nio_opt, opt.nio_len);
		break;

	case NETROUTE:			/* change ip route */
		route = up->uc_route;
		if (cmdp == NULL) 
			up->uc_route = NULL;
		else {
			if (copyin((caddr_t)cmdp, &addr, sizeof addr)) {
				u.u_error = EFAULT;
				break;
			}

			/* substitute new route */

			up->uc_route = h_make(&addr, FALSE);
		}	

		/* free old entry */

		if (route != NULL)
			h_free(route);
		break;

	case NETINIT:			/* init net i/f */
	case NETDISAB:			/* disable net i/f */		
		/* 
		 * Must be super-user!
		 */
		if (!suser()) {
			u.u_error = EPERM;
			break;
		}
		/*
		 * Copy and verify arguments.
		 */
		if (copyin((caddr_t)cmdp, &init, sizeof init)) {
			u.u_error = EFAULT;
			break;
		}
		/*
		 * Look for ifcb to init.
		 */
		for (lp = netcb.n_ifcb_hd; lp != NULL; lp = lp->if_next) {
			p = init.ni_name;
			q = lp->if_name;
			if (init.ni_unit == lp->if_unit) {
				len = 0;
				while (*p == *q && *q && len++ < NIMAX) {
					p++; q++;
				}
				if (len == NIMAX || *p == *q) {
				/*
				 * For init copy address and call dev init
				 * routine directly.  For disable, set
				 * flag for driver, and call netreset to
				 * clear queues and call dev init routine.
				 */
					if (cmd == NETINIT && !lp->if_avail) {
						lp->if_disab = FALSE;
						lp->if_addr = init.ni_addr;
						(*lp->if_init)(lp);
					} else if (cmd == NETDISAB) {
						lp->if_disab = TRUE;
						netreset(lp);
						/*
						 * Tell users on this i/f
						 */
						for (up=netcb.n_ucb_hd;
						     up != NULL; 
						     up = up->uc_next)
							if (up->uc_srcif == lp)
								to_user(up,
									UABORT);
					}
					return;
				}
			}
		}
		u.u_error = EINVAL;
		break;

	default:
		u.u_error = ENETPARM;
	}
}

/*
 * Generate default local port number, if necessary and check if specified 
 * connection is unique.
 */
uniqport(up, lport, fport)
register struct ucb *up;
register u_short lport, fport;
{
	register struct ucb *q;
	register i;
	int deflt = FALSE;

	/* if local port is zero, make up a default */

	if (lport == 0) {
		for (q = netcb.n_ucb_hd, i = 1; q != NULL && q != up;
						q = q->uc_next, i++);
		lport = (i << 8) | (up->uc_proc->p_pid & 0xff);
		deflt++;
	}
		
	/* make sure port number is unique, for defaults, just increment
	   original choice until it is unique. */

tryagain:
	for (q = netcb.n_ucb_hd; q != NULL; q = q->uc_next) 
		if (q != up && q->uc_host.s_addr == up->uc_host.s_addr &&
		    q->uc_local.s_addr == up->uc_local.s_addr &&  
		    ((q->uc_flags & UTCP && q->uc_tcb != NULL &&
		      q->uc_tcb->t_lport == lport &&
		      q->uc_tcb->t_fport == fport) ||
		     (q->uc_flags & UUDP &&
		      q->uc_udp.u_lport == lport &&
		      q->uc_udp.u_fport == fport))) {
			if (deflt)  {
				if (++lport < 0x100)
					lport = 0x100;
				goto tryagain;
			}
			break;
		}
		
	/* if connection is unique, return local port, otherwise set error */

	if (q == NULL)
		return(lport);
	else {
		u.u_error = ENETRNG;
		return(0);
	}
}

/*
 * Block until TCP connection is open or error.
 */
awaitconn(up, tp)
register struct ucb *up;
register struct tcb *tp;
{

	while (up->uc_state == 0 && !tp->syn_acked)
          	sleep(up, PZERO+1);

	if (up->uc_state != 0) {
		if (up->uc_state & UINTIMO)
			u.u_error = ENETTIM;
		else if (up->uc_state & URESET)
			u.u_error = ENETREF;
		else if (up->uc_state & UNRCH)
			u.u_error = ENETUNR;
		else if (up->uc_state & UDEAD)
			u.u_error = ENETDED;
		else
			u.u_error = ENETSTAT;
	} else if (!tp->syn_acked)
		u.u_error = EINTR;
}