BBN-Vax-TCP/bbnnet-oct82/netuser.c
#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;
}