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

#include "../h/param.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/net.h"
#include "../bbnnet/ifcb.h"
#include "../bbnnet/tcp.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/imp.h"
#include "../bbnnet/ucb.h"
#include "../bbnnet/fsm.h"

/*
 * TCP finite state machine primitives
 *
 * These routines are called from the procedures in tcp_procs.c to do low
 * level protocol functions.
 */

/*
 * Send a tcp segment
 */
send_pkt(tp, flags, len, dat)        
register struct tcb *tp;
register int flags;
int len;
struct mbuf *dat;
{
	register struct mbuf *m;
	register struct th *t;
	register struct ucb *up;
	register i;
	short *p;
	struct work w;

	if ((m = m_get(1)) == NULL)
		return(FALSE);
	up = tp->t_ucb;
	/*
	 * Build tcp leader at bottom of new buffer to leave room for lower
	 * level leaders.  Leave an extra four bytes for TCP max segment size
	 * option, which is sent in SYN packets.
	 */
	m->m_off = MSIZE - sizeof(struct th) - 4;
	m->m_len = sizeof(struct th);
	m->m_next = dat;
	t = mtod(m, struct th *);
	/*
	 * Adjust data length for SYN and FIN.  Also, insert max seg size
	 * option for SYN.
	 */
	if (flags&T_FIN)
		len--;
	if (flags&T_SYN)
#ifdef NOTCPOPTS
		len--;
#else
	{
		m->m_len += TCP_MAXSEG_OPTLEN;
		len += TCP_MAXSEG_OPTLEN - 1;	/* SYN occupies seq space */	
		t->t_off = (TCPSIZE + TCP_MAXSEG_OPTLEN) >> 2;
		p = (short *)((int)t + sizeof(struct th));
		*p++ = TCP_MAXSEG_OPTHDR;
		*p = short_to_net(up->uc_srcif->if_mtu - TCPIPMAX);
	} else 
#endif
		t->t_off = TCPSIZE >> 2;
	t->t_len = short_to_net(len + TCPSIZE);
	/*
	 * Set up Internet addresses in IP part of leader, and fill in
	 * the TCP part.
	 */
	t->t_s = up->uc_local;
	t->t_d = up->uc_host;
	t->t_src = short_to_net(tp->t_lport);
	t->t_dst = short_to_net(tp->t_fport);
	t->t_seq = long_to_net(tp->snd_nxt);
	t->t_ackno = long_to_net(tp->rcv_nxt);

	if (tp->snd_rst) {
		flags |= T_RST;
		flags &= ~T_SYN;
	}
	if (tp->snd_urg)
		flags |= T_URG;
	if (tp->syn_rcvd)
		flags |= T_ACK;
	t->t_flags = flags;
	/*
	 * If we sent a zero window, we should try to send a non-zero ACK ASAP.
	 */
	if ((i = rcv_resource(tp)) == 0)
		tp->sent_zero = TRUE;
	else
		tp->sent_zero = FALSE;

	t->t_win = short_to_net(i);
	t->t_urp = short_to_net(tp->snd_urp-tp->snd_nxt);
	/*
	 * Get rest of IP part of leader ready for checksum and IP processing.
	 */
	t->t_next = NULL;
	t->t_prev = NULL;
	t->t_x1 = 0;
	t->t_x2 = 0;
	t->t_sum = 0;
	t->t_pr = TCPROTO;

#ifndef mbb
	t->t_sum = cksum(m, len + sizeof(struct th));
#else
	t->t_x0 = 0;
	t->t_x00 = 0;
	i = cksum(m, len + sizeof(struct th));
	t->t_sum = short_to_net(i);
#endif mbb

	i = ip_send(up, m, TCPROTO, len+TCPSIZE, tp->t_optlen, tp->t_opts, 
		    FALSE);

	if (up->uc_flags & UDEBUG) {
		w.w_dat = (char *)t;
		w.w_stype = i;
		tcp_debug(tp, &w, INRECV, -1);
	}
	return(i);
}

/*
 * Find the first empty spot in rcv buffer 
 */
sequence firstempty(tp)                  
register struct tcb *tp;
{
	register struct th *p, *q;

	if ((p = tp->t_rcv_next) == (struct th *)tp || 
	    SEQ_LT(tp->rcv_nxt, p->t_seq))
		return(tp->rcv_nxt);

	while ((q = p->t_next) != (struct th *)tp &&    
	       SEQ_EQ(t_end(p)+1, q->t_seq))
		p = q;

	return(t_end(p) + 1);
}

/*
 * Get number of rcv bufs available 
 */
rcv_resource(tp)                       
register struct tcb *tp;
{
	register struct ucb *up = tp->t_ucb; 
	register struct th *t;
	register i;

	/* first count bufs in user receive queue */

	i = (up->uc_rcv - up->uc_rsize) * MLEN;  

	/* now reduce by segments in sequencing queue */

	for (t = tp->t_rcv_next; i > 0 && t != (struct th *)tp; t = t->t_next)
		i -= t->t_len;

	return(i < 0 ? 0 : i);
}

/*
 * Copy mbuf chain from snd buffer for sends
 */
struct mbuf *snd_copy(tp, start, end)
struct tcb *tp;
sequence start, end;
{
	register struct mbuf *m, *n;
	register sequence off;
	register adj, len;
	struct mbuf *top;

	/* make sure we have something to copy */

	if (SEQ_GEQ(start, end))    
		return(NULL);

	off = tp->snd_una;
	if (!tp->syn_acked)		/* skip over SYN */
		off++;
	m = tp->t_ucb->uc_sbuf;

	/* find mbuf to start copying */

	while (m != NULL && SEQ_GEQ(start, off+m->m_len)) {
		off += m->m_len;
		m = m->m_next;
	}

	/* get buffer to copy into */

	if (m == NULL || (n = top = m_get(1)) == NULL)
		return(NULL);

	adj = start - off;
	off = start;
	goto partial;		/* start with partial copy */

	do {		/* main copy loop */

		/* amount we can copy into mbuf */

		if ((adj = MLEN - n->m_len) >= m->m_len)
			len = m->m_len;		/* can copy all */
		else
			len = adj;		/* partial copy */

		/* copy as much as possible into mbuf */

		bcopy((caddr_t)((int)m + m->m_off), 
			(caddr_t)((int)n + MHEAD + n->m_len), len);
		n->m_len += len;
		off += len;

		/* must get another mbuf to copy into */

		if (SEQ_GT(end, off) && adj < m->m_len) {
			if ((n = n->m_next = m_get(1)) == NULL) {
				m_freem(top);
				return(NULL);
			}

			/* partial copy into new mbuf */

partial:		n->m_off = MHEAD;
			n->m_next = NULL;
			n->m_len = m->m_len - adj;
			bcopy((caddr_t)((int)m + m->m_off + adj),
				(caddr_t)((int)n + MHEAD), n->m_len);
			off += n->m_len;
		}

	} while (SEQ_GT(end, off) && (m = m->m_next) != NULL);

	/* make sure there was enough in buffer to copy */

	if (m == NULL) {
		printf("snd_copy: bad copy\n");
		m_freem(top);
		return(FALSE);
	}

	/* adjust length of last mbuf copied */

	n->m_len -= (off - end);
	return(top);
}


/*
 * Cancel a timer
 */
t_cancel(tp, timer)                     
register struct tcb *tp;
register timer;
{
	register struct work *w;

	/* reset timer value in tcb */

	tp->t_timers[timer] = 0;

	/* remove any timer work entries already enqueued */

	for (w = netcb.n_work; w != NULL; w = w->w_next) {
		if (w->w_tcb == tp && w->w_type == ISTIMER && 
				w->w_stype == timer)
			w->w_type = INOP;
		
		/* THIS SHOULD GO AWAY */

		if (w == w->w_next) 
			w->w_next = NULL;
	}
}

/*
 * TCP timer update routine 
 */
tcp_timeo()                     
{
	register struct tcb *tp;
	register i;

	/* search through tcb and update active timers */

	for (tp = netcb.n_tcb_head; tp != NULL; tp = tp->t_tcb_next) {
		for (i = TINIT; i <= TFINACK; i++)
			if (tp->t_timers[i] != 0 && --tp->t_timers[i] == 0)
				w_alloc(ISTIMER, i, tp, 0);
		tp->t_timers[TXMT]++;
	}
	netcb.n_iss += ISSINCR;         /* increment iss */
}

/*
 * Tell user proc about change of tcp state 
 */
to_user(up, state)              
register struct ucb *up;
register short state;
{

	/* set user state flag and awaken user proc if asleep */

	up->uc_state |= state;
	wakeup(up);

	/* send urgent signal to user process */

  	if (state == UURGENT)  
		psignal(up->uc_proc, SIGURG);
}

/*
 * Do TCP option processing
 */
tcp_opt(tp, t, hlen)        
register struct tcb *tp;
register struct th *t;
int hlen;
{
	register char *p;
	register i, len;

	p = (char *)((int)t + sizeof(struct th));	/* -> at options */
	
	if ((i = hlen - TCPSIZE) > 0) {			/* any options */

		while (i > 0)

  			switch (*p++) {
			case TCP_END_OPT:                 
				return;
			case TCP_NOP_OPT:                 
				i--;
				break;

			case TCP_MAXSEG_OPT:		/* max segment size */
				if (t->t_flags&T_SYN && !tp->syn_rcvd) {
					len = short_from_net(
					          *(short *)((int)p + 1));
					tp->t_maxseg = 
					       MIN(tp->t_ucb->uc_srcif->if_mtu -
						   TCPIPMAX, len);
				}
			
			default:
				i -= *p;
				p += *p - 1;
			}
	}
}