BBN-Vax-TCP/bbnnet/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/tcp.h"
#include "../bbnnet/ucb.h"
#include "../bbnnet/fsm.h"
#include "../bbnnet/tcp_pred.h"

extern struct user u;

/*
 * 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 if_local *lp;
	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 ((snd + rcv + 2) > (NNETPAGES*NMBPG-32-netcb.n_lowat)) {
		u.u_error = ENETBUF;
		return;
	}

	/* allocate a ucb from the connection table */

	for (up = contab; up->uc_proc != NULL && up < conNCON; up++);

	if (up == conNCON) {            /* connection table full */
		u.u_error = ENETCON;
		return;
	}

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

	if ((fp = falloc()) == NULL) {
		return(-1);
	}
	i = u.u_r.r_val1;
	fp->f_flag = (FNET|FREAD|FWRITE);
	fp->f_un.f_ucb = up;
	fp->f_inode = NULL;

	/* init common ucb fields */

	up->uc_proc = u.u_procp;
	up->uc_snd = snd;
	up->uc_rcv = rcv;
	netcb.n_lowat += snd + rcv + 2;
	netcb.n_hiwat = 2 * netcb.n_lowat;
	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_lo = 0;
	up->uc_hi = 0;

	/* 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) {
		for (lp=locnet; lp < &locnet[nlocnet]; lp++)
			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_ifcb->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=locnet; lp < &locnet[nlocnet]; lp++) {
			if (lp->if_ifcb->if_avail) {
				up->uc_srcif = locnet;	
				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=locnet; lp < &locnet[nlocnet]; lp++) 
			if (lp->if_ifcb->if_net==net && lp->if_ifcb->if_avail) {
				up->uc_srcif = lp;
				break;
			}
	}

	up->uc_flags = 0;
	if (cp->c_mode & CONDEBUG)
		up->uc_flags |= UDEBUG;

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

	switch (cp->c_mode & 036) {

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

	case CONRAW:
		mask = URAW;
		up->uc_flags |= cp->c_mode & (RAWCOMP+RAWASIS+RAWERR+RAWRALL);
		goto concom;

	case CONIP:
		mask = UIP;
		up->uc_flags |= cp->c_mode & (RAWFLAG+RAWERR+RAWRALL);
	concom:
		if (raw_range(up, mask, &cp->c_fcon, cp->c_lo, cp->c_hi)) {
        		up->uc_flags |= mask;
        		up->uc_lo = cp->c_lo;
        		up->uc_hi = cp->c_hi;
			break;
		}
		u.u_error = ENETRNG;
		goto conerr;

	case CONCTL:
		up->uc_flags |= UCTL;
		break;

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

/*
 * 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;
	}
	tp = (struct tcb *)((int)m + MHEAD);    /* -> 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;
	tp->t_fport = cp->c_fport;

	/* if local port is 0, make one up, make sure connection will
	   be unique. */

	if (cp->c_lport == 0) {
		tp->t_lport = (((int)(up - contab) + 1) << 8) | 
				(up->uc_proc->p_pid & 0xff);

		/* keep incrementing port until connection is unique */

		while (!uniqport(up, tp)) {
			if (++tp->t_lport < 0x100)
				tp->t_lport = 0x100;
		}
	} else {
		tp->t_lport = cp->c_lport;
		if (!uniqport(up, tp)) {
			u.u_error = ENETRNG;
			m_free(m);
			return;
		}
	}

	up->uc_tcb = tp;
	up->uc_flags |= UTCP;

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

	if ((cp->c_mode & CONACT) == CONACT) 
		w_alloc(IUOPENR, 0, tp, NULL);
	else {
		up->uc_flags |= ULISTEN;
		w_alloc(IUOPENA, 0, tp, NULL);
	}

	/* block until connection has opened or error */

	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 = ENETERR;
	} else if (!tp->syn_acked)
		u.u_error = EINTR;
}

/*
 * 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;
	tp = up->uc_tcb;

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

		/* 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);
		} else
			up->uc_proc = NULL;

	} else {        /* raw connection */

	/* free ucb and lower system buffer allocation */

        	netcb.n_lowat -= up->uc_snd + up->uc_rcv + 2;
        	netcb.n_hiwat = 2 * netcb.n_lowat;
		if (up->uc_route != NULL) {
        		h_free(up->uc_route);
			up->uc_route = NULL;
		}
        	up->uc_proc = NULL;
	}

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

	if (up->uc_state)               /* new status to report */
		u.u_error = ENETSTAT;
	else {
        	switch (up->uc_flags & URMSK) {
        
        	case UTCP:              
        		tcp_read(up);
        		break;
        	case UIP:                                           
        	case URAW:
        		raw_read(up);
        		break;
        	default:
        		u.u_error = ENETPARM;
        	}
	}
}

/*
 * Network write routine. Just call protocol specific write routine.
 */
netwrite(up)
register struct ucb *up;
{
	switch (up->uc_flags & URMSK) {

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

/*
 * TCP read routine.  Copy data from the receive queue to user space.
 */
tcp_read(up)                     /* read data from the net */
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;
	}
	eol = FALSE;
	incount = u.u_count;

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

	        if (up->uc_rsize != 0) {   /* 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;

			/* Must check for bad read buffer count.  Count can
			   be > 0 with no buffers on queue due to a race
			   condition when a foreign close happens. Just
			   correct count and ignore. */

			if (m == NULL) {
				printf("net_read: bad rsize %d ",up->uc_rsize);
				if (up->uc_tcb == NULL)
					printf(" no tcb\n");
				else
					printf(" tcb=%X state=%d\n", up->uc_tcb, up->uc_tcb->t_state);
				up->uc_rsize = 0;
			}

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

	/* return outstanding status only if no data was xfered */

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

/*
 * 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;
	struct {
		struct mbuf *top;
	} nn;

	if ((tp = up->uc_tcb) == NULL) {                    
		u.u_error = ENETERR;
		return;
	}

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

	if (!u.u_error && up->uc_state)         /* outstanding status */
		u.u_error = ENETSTAT;
}

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

	while (up->uc_rsize == 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);
}

/*
 * 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 & URMSK) {

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

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

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

	if (!u.u_error && up->uc_state)         /* outstanding status */
		u.u_error = ENETSTAT;
}

/*
 * 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;
	struct netstate nstat;
	struct {
		int optlen;
		char *options;
	} opt;

	tp = up->uc_tcb;

	switch (cmd) {

	case NETGETS:                   /* get net status */
		bcopy((caddr_t)&up->uc_lo, (caddr_t)&nstat, sizeof(struct netstate));
		if (tp != NULL) {
			nstat.n_lport = tp->t_lport;
			nstat.n_fport = tp->t_fport;
		}
		nstat.n_fcon = up->uc_host;
		nstat.n_lcon = up->uc_srcif->if_addr;

		if (copyout((caddr_t)&nstat, cmdp, sizeof(struct netstate)))
			u.u_error = EFAULT;
		else
			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 NETSETD:                   /* set debugging file */
		if (suser()) {
			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);
					}
				}
			}
		} else
			u.u_error = EPERM;
		break;

	case NETRESET:                  /* forced tcp reset */
		if (suser()) {
			for (up = contab; up < conNCON; up++)
				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 = ENETRNG;
		} else
			u.u_error = EPERM;
		break;

	case NETDEBUG:                  /* toggle debugging flag */
		for (up = contab; up < conNCON; up++)
			if ((up->uc_flags & UTCP) && up->uc_tcb != NULL &&
			    up->uc_tcb == (struct tcb *)cmdp) {
				up->uc_flags ^= UDEBUG;
				return;
			}
		u.u_error = ENETRNG;
		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;
				return;
			}

			/* maximum option string is 40 bytes */

			if (opt.optlen > 40) {
				u.u_error = ENETRNG;
				return;
			}

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

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

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

	case NETGINIT:			/* reread the gateway file */
		if (suser())
			if (gatinit() < 0)
				u.u_error = ENETRNG;
		else
			u.u_error = EPERM;
		break;

	default:
		u.u_error = ENETPARM;
	}
}

/*
 * Check if specified connection is unique.
 */
uniqport(up, tp)
register struct ucb *up;
register struct tcb *tp;
{
	register struct ucb *q;

	for (q = contab; q < conNCON; q++) {
		if (q->uc_proc != NULL && q != up &&
		    q->uc_tcb->t_lport == tp->t_lport &&
		    q->uc_tcb->t_fport == tp->t_fport &&
		    q->uc_host.s_addr == up->uc_host.s_addr &&
		    q->uc_srcif->if_addr.s_addr ==
		      up->uc_srcif->if_addr.s_addr) 
			return(FALSE);
	}

	return(TRUE);
}