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

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

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

/*
 * TCP finite state machine procedures.
 *
 * Called from finite state machine action routines, these do most of the work
 * of the protocol.  They in turn call primitive routines (in tcp_prim) to
 * perform lower level functions.
 */

/*
 * Set up a TCB for a connection
 */
t_open(tp, mode)                
register struct tcb *tp;
int mode;
{
	register struct ucb *up = tp->t_ucb;

	/* enqueue the tcb */

	if (netcb.n_tcb_head == NULL) {
		netcb.n_tcb_head = tp;
		netcb.n_tcb_tail = tp;
	} else {
		tp->t_tcb_next = netcb.n_tcb_head;
		netcb.n_tcb_head->t_tcb_prev = tp;
		netcb.n_tcb_head = tp;
	}

	/* initialize non-zero tcb fields */

	tp->t_rcv_next = (struct th *)tp;
	tp->t_rcv_prev = (struct th *)tp;
	tp->t_xmtime = T_REXMT;
#ifndef NOTCPOPTS
	tp->t_maxseg = MIN(up->uc_srcif->if_mtu - TCPIPMAX, TCPMAXSND);
#else
	tp->t_maxseg =  up->uc_srcif->if_mtu - TCPIPMAX;
#endif
	tp->snd_end = tp->seq_fin = tp->snd_nxt = tp->snd_hi = 
				tp->snd_una = tp->iss = netcb.n_iss;
	netcb.n_iss += (ISSINCR >> 1) + 1;

	/* set timeout for open */

	tp->t_timers[TINIT] = (up->uc_timeo != 0 ? up->uc_timeo : 
					(mode == TCP_ACTIVE ? T_INIT : 0));
	up->uc_timeo = 0;       /* overlays uc_ssize */
}

/*
 * Delete TCB and free all resources used by the connection.  Called after
 * the close protocol is complete.
 */
t_close(tp, state)
register struct tcb *tp;
short state;
{
	register struct ucb *up;
	register struct th *t;
	register struct mbuf *m;
	register struct work *w;
	int i;

	up = tp->t_ucb;

	/* cancel all timers */

	for (i = TINIT; i <= TFINACK; i++)
		tp->t_timers[i] = 0;

	/* remove all work entries for tcb */

	for (w = netcb.n_work; w != NULL; w = w->w_next)
		if (w->w_tcb == tp)
			w->w_type = INOP;

	/* delete tcb */

	if (tp->t_tcb_prev == NULL)
		netcb.n_tcb_head = tp->t_tcb_next;
	else
		tp->t_tcb_prev->t_tcb_next = tp->t_tcb_next;
	if (tp->t_tcb_next == NULL)
		netcb.n_tcb_tail = tp->t_tcb_prev;
	else
		tp->t_tcb_next->t_tcb_prev = tp->t_tcb_prev;

	/* free all data on receive and send buffers */

	for (t = tp->t_rcv_next; t != (struct th *)tp; t = t->t_next)
		m_freem(dtom(t));

	if (up->uc_rbuf != NULL) {
		m_freem(up->uc_rbuf);
		up->uc_rbuf = NULL;
	}

	if (up->uc_sbuf != NULL) {
		m_freem(up->uc_sbuf);
		up->uc_sbuf = NULL;
	}

	for (m = tp->t_rcv_unack; m != NULL; m = m->m_act) {
		m_freem(m);
		tp->t_rcv_unack = NULL;
	}

	/* free tcb buffer */

	m_free(dtom(tp));
	up->uc_tcb = NULL;

	/* lower buffer allocation and decrement host entry */

	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 user has initiated close (via close call), delete ucb
	   entry, otherwise just wakeup so user can issue close call */

	if (tp->usr_abort) {
		if (up->uc_flags & UCWAIT)
			wakeup(tp);
        	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));
	} else
        	to_user(up, state);

}

/*
 * Send a TCP segment.  Send data from left window edge of send buffer up to
 * window size or end (whichever is less).  Set retransmission timers.
 */
send_tcp(tp, ctl)                        
register struct tcb *tp;
int ctl;
{
	register struct ucb *up;
	register sequence last, wind;
	struct mbuf *m;
	int forced, sent;
	int snd_flags;
	struct mbuf *snd_copy();
	int len;

	up = tp->t_ucb;
	snd_flags = 0;
	tp->snd_lst = tp->snd_nxt;
	forced = FALSE;
	m = NULL;
	/* 
	 * Send SYN if this is first data (ISS)
	 */
	if (SEQ_EQ(tp->snd_nxt, tp->iss)) {	
		snd_flags |= T_SYN;	
		tp->snd_lst++;
	} 
	/*
	 * Get seq # of last datum in send buffer 
	 */
	last = tp->snd_una;
	if (!tp->syn_acked)
		last++;				/* don't forget SYN */
	for (m = up->uc_sbuf; m != NULL; m = m->m_next) 
		last += m->m_len;
	/*
	 * If no data to send in buffer, just do FIN check, otherwise see
	 * how much we should send in segment.
	 */
	if (SEQ_GEQ(tp->snd_nxt, last)) {
		/*
		 * should send FIN?  don't unless haven't already sent one 
		 */
		if (tp->snd_fin && 
		    (SEQ_EQ(tp->seq_fin, tp->iss) || 
		     SEQ_LEQ(tp->snd_nxt, tp->seq_fin))) {
			snd_flags |= T_FIN;
			tp->seq_fin = tp->snd_lst++;
		}
	} else if (tp->syn_acked) { 
		/*
		 * Only send a segment if there is something in the buffer,
		 * and a window has been received.
		 */
		wind = tp->snd_una + tp->snd_wnd;

		/* Use window to limit send */

		tp->snd_lst = SEQ_MIN(last, wind);

		/* Make sure we don't do IP fragmentation or send
		   more than peer can handle */

		if ((len = tp->snd_lst - tp->snd_nxt) > tp->t_maxseg) 
			tp->snd_lst -= len - tp->t_maxseg;

		/* see if PUSH should be sent */

		if (SEQ_GT(tp->snd_end, tp->iss) && 
		    SEQ_LEQ(tp->snd_end, tp->snd_lst)) 
			snd_flags |= T_PUSH;

		/* Avoid silly window syndrome: make sure usable window
		   is at least 25% of offered window.  Be sure the user
		   is not blocked for send resources. 
		
		else if (!tp->rexmt && !tp->ack_due && !tp->snd_fin &&
			 up->uc_snd != up->uc_ssize && tp->snd_wnd > 0 && 
			 100*((tp->snd_lst-tp->snd_una)/tp->snd_wnd) < 25)
			tp->snd_lst = tp->snd_nxt; 

		/* Set persist timer, if we exceeded the window */

		if (SEQ_GEQ(tp->snd_lst, wind))                       
			tp->t_timers[TPERSIST] = T_PERS;

		/* Check if window is closed and must force a byte out 
		   (persist timer went off) */

		if (tp->force_one && SEQ_EQ(tp->snd_lst, wind)) {
			tp->snd_lst = tp->snd_nxt + 1;
			forced = TRUE;
		}

		/* copy data to send from send buffer */

		m = snd_copy(tp, SEQ_MAX(tp->iss+1,tp->snd_nxt), tp->snd_lst);

		/* must send FIN and no more data left to send after this */

		if (tp->snd_fin && !forced && SEQ_EQ(tp->snd_lst, last) &&
		    (SEQ_EQ(tp->seq_fin, tp->iss) || 
		     SEQ_LEQ(tp->snd_nxt, tp->seq_fin))) {
			snd_flags |= T_FIN;
			tp->seq_fin = tp->snd_lst++;
		}
	}
	/*
	 * If there is something to send, do it and update timers for rexmt.
	 */
	if (SEQ_LT(tp->snd_nxt, tp->snd_lst)) {        

		sent = send_pkt(tp, snd_flags, 
				    (int)(tp->snd_lst-tp->snd_nxt), m);  

		/* set timers for retransmission if necessary */

		if (!forced) {
			tp->t_timers[TREXMT] = tp->t_xmtime;
			tp->t_rexmt_val = tp->snd_lst;
			if (!tp->rexmt) {
				tp->t_timers[TREXMTTL] = T_REXMTTL;
				tp->t_rtl_val = tp->snd_lst;
			}
		}

		/* update seq for next send if this one got out */

		if (sent)
			tp->snd_nxt = tp->snd_lst;                

		/* if last timed message has been acked, start timing
		   this one */

		if (tp->syn_acked && SEQ_GT(tp->snd_una, tp->t_xmt_val)) {
			tp->t_timers[TXMT] = 0;
			tp->t_xmt_val = tp->snd_lst;
		}

		tp->ack_due = FALSE;
		tp->snd_hi = SEQ_MAX(tp->snd_nxt, tp->snd_hi);
		tp->rexmt = FALSE;
		tp->force_one = FALSE;
		if (sent)
			return(TRUE);
	}
	/*
	 * If ctl, make sure to send something so ACK gets through 
	 */
	if (ctl == TCP_CTL) {
		send_pkt(tp, 0, 0, NULL);
		tp->ack_due = FALSE;
	}
	return(FALSE);
}

/*
 * Process incoming segments 
 */
rcv_tcp(tp, n, ctl)                 
register struct tcb *tp;
register struct th *n;
int ctl;
{
	register struct mbuf *m;
	register sequence last;
	int sent;

	tp->dropped_txt = FALSE;
	tp->ack_due = FALSE;
	tp->new_window = FALSE;
	/*
	 * Process SYN
	 */
	if (!tp->syn_rcvd && n->t_flags&T_SYN) {	
		tp->irs = n->t_seq;
		tp->rcv_nxt = n->t_seq + 1;
		tp->snd_wl = tp->rcv_urp = tp->irs;
		tp->syn_rcvd = TRUE;
		tp->ack_due = TRUE;
	}
	/*
	 * Process ACK if data not already acked previously. (Take ACKed data
	 * off send queue, and reset rexmt timers).
	 */
	if (n->t_flags&T_ACK && tp->syn_rcvd && SEQ_GT(n->t_ackno, tp->snd_una))
        	rcv_ack(tp, n);
	/*
	 * Check for new window.
	 */
	if (tp->syn_rcvd && SEQ_GEQ(n->t_seq, tp->snd_wl)) {	
		tp->snd_wl = n->t_seq;
		tp->snd_wnd = n->t_win;
		tp->new_window = TRUE;
		t_cancel(tp, TPERSIST);         /* cancel persist timer */  
	}
	/*
	 * For data packets only (vs. ctl), process data and URG.
	 */
	if (ctl == TCP_DATA) {				
		if (n->t_len != 0)
			rcv_text(tp, n);	/* accept and sequence data */
		/*
		 * Process URG if new urgent pointer received.
		 */
		if (n->t_flags&T_URG) {			
			last = n->t_urp + n->t_seq;
			if (SEQ_LT(tp->rcv_nxt, last)) {

				/* Tell user about start of urgent data only */

				if (SEQ_LEQ(tp->rcv_urp, tp->rcv_nxt))
					to_user(tp->t_ucb, UURGENT);
				tp->rcv_urp = last;
			}
		}
		/*
		 * Process PUSH, mark end of data chain.
		 */
		if (n->t_flags&T_PUSH && !tp->dropped_txt &&
		    tp->t_rcv_prev != (struct th *)tp) {	

			/* Find last mbuf on received data chain and mark */

			m = dtom(tp->t_rcv_prev);
			if (m != NULL) {
				while (m->m_next != NULL)
					m = m->m_next;
				m->m_act = (struct mbuf *)(m->m_off+m->m_len-1);
			}
		}
	}
	/*
	 * Process FIN, check for duplicates and make sure all data is in.
	 */
	if (n->t_flags&T_FIN && !tp->dropped_txt) {		
		if (tp->fin_rcvd) 
			tp->ack_due = TRUE;
		else {
			/*
			 * Check if we really have FIN 
			 * (rcv buf filled in, no drops) 
			 */
			last = firstempty(tp);
			if ((tp->t_rcv_prev == (struct th *)tp &&
			     SEQ_EQ(last, t_end(n)+1)) || 
			    SEQ_EQ(last, t_end(tp->t_rcv_prev))) {
				tp->fin_rcvd = TRUE;
				wakeup(tp->t_ucb);
			}
			/* 
			 * If FIN, then set to ACK: incr rcv_nxt, since FIN 
			 * occupies sequence space 
			 */
			if (tp->fin_rcvd && SEQ_GEQ(tp->rcv_nxt, last)) {
				tp->rcv_nxt = last + 1;
				tp->ack_due = TRUE;
			}
		}
	}
	/*
	 * If ACK required or rcv window has changed, try to send something.
	 */
	sent = FALSE;
	if (tp->ack_due)                
		sent = send_tcp(tp, TCP_CTL);
	else if (tp->new_window)       
		sent = send_tcp(tp, TCP_DATA);
	/*	
	 *  Set rexmt and round trip timers, if anything sent, and unacked
	 *  data left in queue.
	 */
	if (!sent && SEQ_LT(tp->snd_una, tp->snd_nxt) && tp->cancelled) {
		tp->t_timers[TREXMT] = tp->t_xmtime;
		tp->t_timers[TREXMTTL] = T_REXMTTL;
		tp->t_rexmt_val = tp->t_rtl_val = tp->snd_lst;
		tp->cancelled = FALSE;
	}
}

/*
 * Process incoming ACKs.  Remove data from send queue up to acknowledgement.
 * Also handles round-trip timer for retransmissions and acknowledgement of
 * SYN.
 */
rcv_ack(tp, n)          
register struct tcb *tp;
register struct th *n;
{
	register struct ucb *up;
	register struct mbuf *m;
	register len;

	up = tp->t_ucb;

	len = n->t_ackno - tp->snd_una; 
	tp->snd_una = n->t_ackno;
	if (SEQ_GT(tp->snd_una, tp->snd_nxt)) 
		tp->snd_nxt = tp->snd_una;

	/* if timed message has been acknowledged, use the time to set
	   the retransmission time value */

	if (tp->syn_acked && SEQ_GT(tp->snd_una, tp->t_xmt_val)) {
		tp->t_xmtime = (tp->t_timers[TXMT]>T_REXMT ? tp->t_timers[TXMT]
							   : T_REXMT);
		if (tp->t_xmtime > T_REMAX)
			tp->t_xmtime = T_REMAX;
	}

	/* handle ack of opening syn (tell user) */

	if (!tp->syn_acked && SEQ_GT(tp->snd_una, tp->iss)) {
		tp->syn_acked = TRUE;
		len--;			/* ignore SYN */
		t_cancel(tp, TINIT);	/* cancel init timer */
	}

	/* remove acknowledged data from send buff */

	m = up->uc_sbuf;
	while (len > 0 && m != NULL) 
		if (m->m_len <= len) {
			len -= m->m_len;
			m = m_free(m);
			up->uc_ssize--;
		} else {
			m->m_len -= len;
			m->m_off += len;
			break;
		}

	up->uc_sbuf = m;
	wakeup(tp->t_ucb);

	/* handle ack of closing fin */

	if (SEQ_NEQ(tp->seq_fin, tp->iss) && SEQ_GT(tp->snd_una, tp->seq_fin))
		tp->snd_fin = FALSE;
	t_cancel(tp, TREXMT);          /* cancel retransmit timer */
	t_cancel(tp, TREXMTTL);        /* cancel retransmit too long timer */
	tp->cancelled = TRUE;      
}

/*
 * Process incoming data.  Put the segments on sequencing queue in order,
 * taking care of overlaps and duplicates.  Data is removed from sequence
 * queue by present_data when sequence is complete (no holes at top).
 * Drop data that falls outside buffer quota if tight for space.  Otherwise,
 * process and recycle data held in tcp_input.
 */
rcv_text(tp, t)                 
register struct tcb *tp;
register struct th *t;
{
	register i;
	register struct th *p, *q;
	register struct mbuf *m, *n;
	struct ucb *up;
	struct th *savq;
	int j;
	sequence last;

	/* throw away any data we have already received */

	if ((i = tp->rcv_nxt - t->t_seq) > 0)  {
		if (i < t->t_len) {
        		t->t_seq += i;
        		t->t_len -= i;
        		m_adj(dtom(t), i);
		} else {
			tp->ack_due = TRUE;	/* send ack just in case */
			return;
		}
	}

	last = t_end(t);                /* last seq # in incoming seg */

	/* # buffers available to con */

	up = tp->t_ucb;
	i = (int)up->uc_rcv - (int)up->uc_rsize;
	if (i < 0)
		i = 0;
	        
	/* count buffers in segment */
	        
	for (m = dtom(t), j = 0; m != NULL; m = m->m_next)
		if (m->m_len != 0)
        		j++;
	        
	/* not enough resources to process segment */

	if (j > i && netcb.n_bufs < netcb.n_lowat) {            

		/* if segment preceeds top of seqeuncing queue, try to take
		   buffers from bottom of queue */

                q = tp->t_rcv_next;             
		if (q != (struct th *)tp && SEQ_LT(tp->rcv_nxt, q->t_seq) &&
		    SEQ_LT(t->t_seq, q->t_seq)) 

			for (p = tp->t_rcv_prev; i < j &&
			     p != (struct th *)tp;) {
				savq = p->t_prev;
				tcp_deq(p);
				i += m_freem(dtom(p));
				p = savq;
			}

		/* if still not enough room, drop text from end of segment */

		if (j > i) {

			for (m = dtom(t); i > 0 && m != NULL; i--)
				m = m->m_next;

        		while (m != NULL) {
        			t->t_len -= m->m_len;
        			last -= m->m_len;
        			m->m_len = 0;
        			m = m->m_next;
        		}
        		tp->dropped_txt = TRUE;
        		if (SEQ_LT(last, t->t_seq))
        			return;
        	}       
	}

	/* merge incoming data into the sequence queue */

        q = tp->t_rcv_next;             /* -> top of sequencing queue */
	        
        /* skip frags which new doesn't overlap at end */
                
        while ((q != (struct th *)tp) && SEQ_GT(t->t_seq, t_end(q)))
        	q = q->t_next;
                
        if (q == (struct th *)tp) {     /* frag at end of chain */

		if (SEQ_GEQ(last, tp->rcv_nxt)) {
		        tp->net_keep = TRUE;
        	        tcp_enq(t, tp->t_rcv_prev);
		}

        } else {  

		/* frag doesn't overlap any on chain */

        	if (SEQ_LT(last, q->t_seq)) {  
			tp->net_keep = TRUE;
        		tcp_enq(t, q->t_prev);
                
        	/* new overlaps beginning of next frag only */
                
        	} else if (SEQ_LT(last, t_end(q))) {
        		if ((i = last - q->t_seq + 1) < t->t_len) {
                		t->t_len -= i;
        			m_adj(dtom(t), -i);
				tp->net_keep = TRUE;
        			tcp_enq(t, q->t_prev);
        		} 
                
        	/* new overlaps end of previous frag */
                
        	} else {
        		savq = q;
        		if (SEQ_LEQ(t->t_seq, q->t_seq)) {  /* complete cover */
        			savq = q->t_prev;
        			tcp_deq(q);
        			m_freem(dtom(q));
        		  
        		} else {                        /* overlap */
        			if ((i = t_end(q) - t->t_seq + 1) < t->t_len) {
                			t->t_seq += i;  
                			t->t_len -= i;  
                			m_adj(dtom(t), i);
				} else 
					t->t_len = 0;
        		}
                
        	/* new overlaps at beginning of successor frags */
                
        		q = savq->t_next;
        		while ((q != (struct th *)tp) && (t->t_len != 0) && 
        		       SEQ_LT(q->t_seq, last))
                
        			/* complete cover */
                
        			if (SEQ_LEQ(t_end(q), last)) {
        				p = q->t_next;
        				tcp_deq(q);
        				m_freem(dtom(q));
        				q = p;
                
        			} else {        /* overlap */
                
        				if ((i = last-q->t_seq+1) < t->t_len) {
                				t->t_len -= i;
                				m_adj(dtom(t), -i);
					} else
						t->t_len = 0;
        				break;
        			}
                
        	/* enqueue whatever is left of new before successors */
                
        		if (t->t_len != 0) {
				tp->net_keep = TRUE;
        			tcp_enq(t, savq);
			}
        	}
        }

	/* set to ack completed data (no gaps) */

	tp->rcv_nxt = firstempty(tp);
	tp->ack_due = TRUE;

	/* if any room remaining in rcv buf, take any unprocessed
	   messages and schedule for later processing */

	if ((m = tp->t_rcv_unack) != NULL && 
	    (i = (int)up->uc_rcv - (int)up->uc_rsize) > 0 &&
	    netcb.n_bufs > netcb.n_lowat)
		do {
		
			/* schedule work request */

			t = mtod(m, struct th *);
			j = (t->t_off << 2) + sizeof(struct ip);
			m->m_off += j;
			m->m_len -= j;
			tp->t_rcv_unack = m->m_act;
			m->m_act = (struct mbuf *)0;
			netstat.t_unack++;
			w_alloc(INRECV, 0, tp, t);     

			/* remaining buffer space */

			for (n = m; n != NULL; n = n->m_next)
				i--;
		} while ((m = tp->t_rcv_unack) != NULL && i > 0);        
}

/*
 * Send a reset segment
 */
send_rst(tp, n)                         
register struct tcb *tp;
register struct th *n;
{
	register struct ucb *up;
	struct socket src, dst;
	u_short port;
	int temp_rst;

        /* don't send a reset in response to a reset */

	if (n->t_flags&T_RST || (up = tp->t_ucb) == NULL)
		return;

	tp->snd_rst = TRUE;
	temp_rst = FALSE;
	if (n->t_flags&T_ACK)
		tp->snd_nxt = n->t_ackno;

	/* if reset required from "wildcard" listener, take addresses and
	   port from incoming packet */

	if (up->uc_local.s_addr == 0 || up->uc_host.s_addr == 0 || 
	    tp->t_fport == 0) {
		src = up->uc_local;
		dst = up->uc_host;
		port = tp->t_fport;
		up->uc_local = n->t_d;
		up->uc_host = n->t_s;
		tp->t_fport = n->t_src;
		temp_rst = TRUE;
	}
	tp->syn_rcvd = FALSE;
	send_pkt(tp, 0, 0, NULL);
	tp->ack_due = FALSE;
	tp->snd_rst = FALSE;

	/* restore "wildcard" addresses */

	if (temp_rst) {
		up->uc_local = src;
		up->uc_host = dst;
		tp->t_fport = port;
		tp->snd_nxt = tp->iss;
		if (up->uc_route != NULL) {
			h_free(up->uc_route);
			up->uc_route = NULL;
		}
	}
}

/*
 * Accept data for the user to receive.  Moves data from sequenced tcp
 * segments from the sequencing queue to the user's receive queue (in the
 * ucb).  Observes locking on receive queue.
 */
present_data(tp)                
register struct tcb *tp;
{
	register struct th *t;
	register struct ucb *up;
	register struct mbuf *m, *n, *top, *bot;
	register sequence ready;
	int i;

	up = tp->t_ucb;                 /* -> ucb */

	/* connection must be synced and data available for user */

	if (tp->syn_acked && (t = tp->t_rcv_next) != (struct th *)tp) {

		/* lock the user's receive buffer */

		while (up->uc_flags & ULOCK)
			sleep(&up->uc_flags, PZERO);
		up->uc_flags |= ULOCK;

		/* find the end of the user's receive buffer */

		m = up->uc_rbuf;
		if (m != NULL)
			while (m->m_next != NULL)
				m = m->m_next;

		/* move as many mbufs as possible from tcb to user queue */

		ready = firstempty(tp);
		while (up->uc_rsize < up->uc_rcv && t != (struct th *)tp && 
	  	       SEQ_LT(t_end(t), ready)) {

			/* count mbufs in msg chunk and free null ones */

			bot = NULL;
			for (n = top = dtom(t); n != NULL;) { 
				if (n->m_len == 0) {
					if (n == top)  
						top = n = m_free(n);
					else
						bot->m_next = n = m_free(n);
				} else {
					bot = n;
					up->uc_rsize++;
					n = n->m_next;
				}
			}

			/* chain new data to user receive buf */

			if (m == NULL)
				up->uc_rbuf = top;
			else 
				m->m_next = top;

			if (bot != NULL)
        			m = bot;

			/* dequeue chunk from tcb */

			tcp_deq(t);
			t = t->t_next;
		}

		/* unlock receive queue for user */

		up->uc_flags &= ~ULOCK;
		wakeup(&up->uc_flags);

		/* awaken reader only if any data on user rcv queue */

		if (up->uc_rsize != 0)
			wakeup(up);

		/* let user know about foreign tcp close if no more data */

		if (tp->fin_rcvd && !tp->usr_closed && rcv_empty(tp))
			to_user(up, UCLOSED);  
	}
}