xref: /csrg-svn/sys/netinet/tcp_usrreq.c (revision 4567)
1 /* tcp_usrreq.c 1.4 81/10/21 */
2 
3 #include "../h/param.h"
4 #include "../h/systm.h"
5 #include "../bbnnet/net.h"
6 #include "../bbnnet/tcp.h"
7 #include "../bbnnet/ip.h"
8 #include "../bbnnet/imp.h"
9 #include "../bbnnet/ucb.h"
10 #define TCPFSTAB
11 #include "../bbnnet/fsm.h"
12 #include "../bbnnet/tcp_pred.h"
13 
14 /*
15  * This file contains the top level routines for actions
16  * called by the user and by the clock routines, while input
17  * actions for network data are driven by tcp_input.  The two
18  * main entry points here are tcp_timeo, which decrements tcp timers
19  * each second and runs timer action routines when the timers go off,
20  * and tcp_usrreq, which performs requests generated by or on behalf
21  * of user processes.  Both routines raise the ipl to splnet to block
22  * off tcp_input processing (triggered by imp's) during the work
23  * here.
24  */
25 
26 tcp_timeo()
27 {
28 	register struct tcb *tp;
29 	int s = splnet();
30 COUNT(TCP_TIMEO);
31 
32 	/*
33 	 * Search through tcb's and update active timers.
34 	 */
35 	for (tp = netcb.n_tcb_head; tp != NULL; tp = tp->t_tcb_next) {
36 		if (tp->t_init != 0 && --tp->t_init == 0)
37 			tcp_usrreq(ISTIMER, TINIT, tp, 0);
38 		if (tp->t_rexmt != 0 && --tp->t_rexmt == 0)
39 			tcp_usrreq(ISTIMER, TREXMT, tp, 0);
40 		if (tp->t_rexmttl != 0 && --tp->t_rexmttl == 0)
41 			tcp_usrreq(ISTIMER, TREXMTTL, tp, 0);
42 		if (tp->t_persist != 0 && --tp->t_persist == 0)
43 			tcp_usrreq(ISTIMER, TPERSIST, tp, 0);
44 		if (tp->t_finack != 0 && --tp->t_finack == 0)
45 			tcp_usrreq(ISTIMER, TFINACK, tp, 0);
46 		tp->t_xmt++;
47 	}
48 	netcb.n_iss += ISSINCR;		/* increment iss */
49 	timeout(tcp_timeo, 0, hz);      /* reschedule every second */
50 	splx(s);
51 }
52 
53 tcp_usrreq(input, stype, tp, m)
54 	int input, stype;
55 	register struct tcb *tp;
56 	char *m;
57 {
58 	struct work w;
59 	int s = splnet();
60 	register int nstate;
61 	register struct work *wp = &w;
62 COUNT(TCP_USRREQ);
63 
64 	wp->w_type = input;
65 	wp->w_stype = stype;
66 	wp->w_tcb = tp;
67 	wp->w_dat = m;
68 	nstate = tp->t_state;
69 	tp->net_keep = 0;
70 	acounts[nstate][input]++;
71 	switch (tcp_fstab[nstate][input]) {
72 
73 	default:
74 		printf("tcp: bad state: tcb=%x state=%d input=%d\n",
75 		    tp, tp->t_state, input);
76 		nstate = EFAILEC;
77 		break;
78 
79 	case LIS_CLS:				/* 1 */
80 		t_open(tp, PASSIVE);
81 		nstate = LISTEN;
82 		break;
83 
84 	case SYS_CLS:				/* 2 */
85 		t_open(tp, ACTIVE);
86 		send_ctl(tp);
87 		nstate = SYN_SENT;
88 		break;
89 
90 	case CLS_OPN:				/* 10 */
91 		t_close(tp, UCLOSED);
92 		nstate = CLOSED;
93 		break;
94 
95 	case CL2_CLW:				/* 10 */
96 		tp->snd_fin = 1;
97 		send_ctl(tp);
98 		tp->usr_closed = 1;
99 		nstate = CLOSING2;
100 		break;
101 
102 	case TIMERS:				/* 14,17,34,35,36,37,38 */
103 		nstate = tcp_timers(wp);
104 		break;
105 
106 	case CLS_RWT:				/* 20 */
107 		present_data(tp);
108 		if (rcv_empty(tp)) {
109 			t_close(tp, UCLOSED);
110 			nstate = CLOSED;
111 		} else
112 			nstate = RCV_WAIT;
113 		break;
114 
115 	case FW1_SYR:				/* 24,25 */
116 		tp->snd_fin = 1;
117 		send_ctl(tp);
118 		tp->usr_closed = 1;
119 		nstate = FIN_W1;
120 		break;
121 
122 	case SSS_SND:				/* 40,41 */
123 		nstate = sss_snd(wp);
124 		break;
125 
126 	case SSS_RCV:				/* 42 */
127 		send_ctl(tp);		/* send new window */
128 		present_data(tp);
129 		break;
130 
131 	case CLS_NSY:				/* 44 */
132 		t_close(tp, UABORT);
133 		nstate = CLOSED;
134 		break;
135 
136 	case CLS_SYN:				/* 45 */
137 		tp->snd_rst = 1;
138 		send_null(tp);
139 		t_close(tp, UABORT);
140 		nstate = CLOSED;
141 		break;
142 
143 	case CLS_ACT:				/* 47 */
144 		t_close(tp, UNETDWN);
145 		nstate = CLOSED;
146 		break;
147 
148 	case NOP:
149 		break;
150 
151 	case CLS_ERR:
152 		to_user(tp->t_ucb, UCLSERR);
153 		break;
154 	}
155 	if (tp->t_ucb->uc_flags & UDEBUG)
156 		tcp_debug(tp, wp, input, nstate);
157 #ifdef TCPDEBUG
158 	tcp_prt(tp, input, wp->w_stype, nstate);
159 #endif
160 	/* YECH */
161 	switch (nstate) {
162 
163 	case SAME:
164 		break;
165 
166 	case EFAILEC:
167 		if (m)
168 			m_freem(dtom(m));
169 		break;
170 
171 	default:
172 		tp->t_state = nstate;
173 		break;
174 	}
175 	splx(s);
176 }
177 
178 t_open(tp, mode)                /* set up a tcb for a connection */
179 	register struct tcb *tp;
180 	int mode;
181 {
182 	register struct ucb *up;
183 COUNT(T_OPEN);
184 
185 	/* enqueue the tcb */
186 
187 	if (netcb.n_tcb_head == NULL) {
188 		netcb.n_tcb_head = tp;
189 		netcb.n_tcb_tail = tp;
190 	} else {
191 		tp->t_tcb_next = netcb.n_tcb_head;
192 		netcb.n_tcb_head->t_tcb_prev = tp;
193 		netcb.n_tcb_head = tp;
194 	}
195 
196 	/* initialize non-zero tcb fields */
197 
198 	tp->t_rcv_next = (struct th *)tp;
199 	tp->t_rcv_prev = (struct th *)tp;
200 	tp->t_xmtime = T_REXMT;
201 	tp->snd_end = tp->seq_fin = tp->snd_nxt = tp->snd_hi =
202 	              tp->snd_una = tp->iss = netcb.n_iss;
203 	tp->snd_off = tp->iss + 1;
204 	netcb.n_iss += (ISSINCR >> 1) + 1;
205 
206 	/* set timeout for open */
207 
208 	up = tp->t_ucb;
209 	tp->t_init = (up->uc_timeo != 0 ? up->uc_timeo :
210 					(mode == ACTIVE ? T_INIT : 0));
211 	up->uc_timeo = 0;       /* overlays uc_ssize */
212 }
213 
214 t_close(tp, state)
215 	register struct tcb *tp;
216 	short state;
217 {
218 	register struct ucb *up;
219 	register struct th *t;
220 	register struct mbuf *m;
221 	register struct work *w;
222 COUNT(T_CLOSE);
223 
224 	up = tp->t_ucb;
225 
226 	tp->t_init = tp->t_rexmt = tp->t_rexmttl = tp->t_persist =
227 	    tp->t_finack = 0;
228 
229 	/* delete tcb */
230 
231 	if (tp->t_tcb_prev == NULL)
232 		netcb.n_tcb_head = tp->t_tcb_next;
233 	else
234 		tp->t_tcb_prev->t_tcb_next = tp->t_tcb_next;
235 	if (tp->t_tcb_next == NULL)
236 		netcb.n_tcb_tail = tp->t_tcb_prev;
237 	else
238 		tp->t_tcb_next->t_tcb_prev = tp->t_tcb_prev;
239 
240 	/* free all data on receive and send buffers */
241 
242 	for (t = tp->t_rcv_next; t != (struct th *)tp; t = t->t_next)
243 		m_freem(dtom(t));
244 
245 	if (up->uc_rbuf != NULL) {
246 		m_freem(up->uc_rbuf);
247 		up->uc_rbuf = NULL;
248 	}
249 	if (up->uc_sbuf != NULL) {
250 		m_freem(up->uc_sbuf);
251 		up->uc_sbuf = NULL;
252 	}
253 	for (m = tp->t_rcv_unack; m != NULL; m = m->m_act) {
254 		m_freem(m);
255 		tp->t_rcv_unack = NULL;
256 	}
257 	m_free(dtom(tp));
258 	up->uc_tcb = NULL;
259 
260 	/* lower buffer allocation and decrement host entry */
261 
262 	netcb.n_lowat -= up->uc_snd + up->uc_rcv + 2;
263 	netcb.n_hiwat = 2 * netcb.n_lowat;
264 	if (up->uc_host != NULL) {
265 		h_free(up->uc_host);
266 		up->uc_host = NULL;
267 	}
268 
269 	/* if user has initiated close (via close call), delete ucb
270 	   entry, otherwise just wakeup so user can issue close call */
271 
272 	if (tp->usr_abort)
273         	up->uc_proc = NULL;
274 	else
275         	to_user(up, state);
276 
277 }
278 
279 sss_snd(wp)
280 	struct work *wp;
281 {
282 	register struct tcb *tp;
283 	register struct mbuf *m, *n;
284 	register struct ucb *up;
285 	register off;
286 	sequence last;
287 
288 	tp = wp->w_tcb;
289 	up = tp->t_ucb;
290 
291 	last = tp->snd_off;
292 
293 	/* count number of mbufs in send data */
294 
295 	for (m = n = (struct mbuf *)wp->w_dat; m != NULL; m = m->m_next) {
296 		up->uc_ssize++;
297 		last += m->m_len;
298 	}
299 
300 	/* find end of send buffer and append data */
301 
302 	if ((m = up->uc_sbuf) != NULL) {		/* something in send buffer */
303 		while (m->m_next != NULL) {		/* find the end */
304 			m = m->m_next;
305 			last += m->m_len;
306 		}
307 		last += m->m_len;
308 
309 		/* if there's room in old buffer for new data, consolidate */
310 
311 		off = m->m_off + m->m_len;
312 		while (n != NULL && (MSIZE - off) >= n->m_len) {
313 			bcopy((caddr_t)((int)n + n->m_off),
314 			      (caddr_t)((int)m + off), n->m_len);
315 			m->m_len += n->m_len;
316 			off += n->m_len;
317 			up->uc_ssize--;
318 			n = m_free(n);
319 		}
320 		m->m_next = n;
321 
322 	} else		/* nothing in send buffer */
323 		up->uc_sbuf = n;
324 
325 	if (up->uc_flags & UEOL) {		/* set EOL */
326 		tp->snd_end = last;
327 	}
328 
329 	if (up->uc_flags & UURG) {		/* urgent data */
330 		tp->snd_urp = last+1;
331 		tp->snd_urg = 1;
332 	}
333 
334 	send(tp);
335 
336 	return (SAME);
337 }
338 
339 tcp_timers(wp)
340 	struct work *wp;
341 {
342 	register struct tcb *tp = wp->w_tcb;
343 	register type = wp->w_stype;
344 
345 COUNT(TCP_TIMERS);
346 	switch (type) {
347 
348 	case TINIT:		/* initialization timer */
349 		if (!tp->syn_acked) {				/* 35 */
350 			t_close(tp, UINTIMO);
351 			return (CLOSED);
352 		}
353 		return (SAME);
354 
355 	case TFINACK:		/* fin-ack timer */
356 		switch (tp->t_state) {
357 
358 		case TIME_WAIT:
359 			/*
360 			 * We can be sure our ACK of foreign FIN was rcvd,
361 			 * and can close if no data left for user.
362 			 */
363 			if (rcv_empty(tp)) {
364 				t_close(tp, UCLOSED);		/* 14 */
365 				return (CLOSED);
366 			}
367 			return (RCV_WAIT);			/* 17 */
368 
369 		case CLOSING1:
370 			tp->waited_2_ml = 1;
371 			return (SAME);
372 
373 		default:
374 			return (SAME);
375 		}
376 
377 	case TREXMT:		/* retransmission timer */
378 		if (tp->t_rexmt_val > tp->snd_una) {	 	/* 34 */
379 			/*
380 			 * Set up for a retransmission, increase rexmt time
381 			 * in case of multiple retransmissions.
382 			 */
383 			tp->snd_nxt = tp->snd_una;
384 			tp->rexmt = 1;
385 			tp->t_xmtime = tp->t_xmtime << 1;
386 			if (tp->t_xmtime > T_REMAX)
387 				tp->t_xmtime = T_REMAX;
388 			send(tp);
389 		}
390 		return (SAME);
391 
392 	case TREXMTTL:		/* retransmit too long */
393 		if (tp->t_rtl_val > tp->snd_una)		/* 36 */
394 			to_user(tp->t_ucb, URXTIMO);
395 		/*
396 		 * If user has already closed, abort the connection.
397 		 */
398 		if (tp->usr_closed) {
399 			t_close(tp, URXTIMO);
400 			return (CLOSED);
401 		}
402 		return (SAME);
403 
404 	case TPERSIST:		/* persist timer */
405 		/*
406 		 * Force a byte send through closed window.
407 		 */
408 		tp->force_one = 1;				/* 38 */
409 		send(tp);
410 		return (SAME);
411 	}
412 	panic("tcp_timers");
413 }
414 
415 tcp_debug(tp, wp, input, newstate)   /* write a debugging record */
416 	register struct tcb *tp;
417 	register struct work *wp;
418 	int input, newstate;
419 {
420 	struct t_debug debuf;
421 	register struct th *n;
422 	register off_t siz;
423 COUNT(TCP_DEBUG);
424 
425 	/* look for debugging file inode */
426 
427 	if (netcb.n_debug == 0)
428 		return;
429 	debuf.t_tod = time;
430 	if ((debuf.t_tcb = tp) != NULL)
431 		debuf.t_old = tp->t_state;
432 	else
433 		debuf.t_old = 0;
434 	debuf.t_inp = input;
435 	debuf.t_tim = wp->w_stype;
436 	debuf.t_new = newstate;
437 	if (input == INRECV) {
438 		n = (struct th *)wp->w_dat;
439 		debuf.t_sno = n->t_seq;
440 		debuf.t_ano = n->t_ackno;
441 		debuf.t_wno = n->t_win;
442 		if (debuf.t_new == -1)
443 			debuf.t_lno = ((struct ip *)n)->ip_len;
444 		else
445 			debuf.t_lno = n->t_len;
446 		debuf.t_flg = *(char *)((int)&n->t_win - 1);
447 	}
448 	/* STUFF INTO CIRC BUFFER */
449 }
450 
451 #ifdef TCPDEBUG
452 
453 tcp_prt(tp, input, timer, newstate)     /* print debugging info on the console */
454 register struct tcb *tp;
455 register input, timer, newstate;
456 {
457 	register oldstate;
458 COUNT(TCP_PRT);
459 
460 	oldstate = tp->t_state;
461 	printf("TCP(%X) %s X %s", tp, tcpstates[oldstate], tcpinputs[input]);
462 	if (input == ISTIMER)
463 		printf("(%s)", tcptimers[timer]);
464 	printf(" --> %s", tcpstates[ (newstate > 0) ? newstate : oldstate ]);
465 	if (newstate < 0)
466 		printf(" (FAILED)\n");
467 	else
468 		putchar('\n');
469 }
470 #endif
471 
472 /* THIS ROUTINE IS A CROCK */
473 to_user(up, state)
474 	register struct ucb *up;
475 	register short state;
476 {
477 COUNT(TO_USER);
478 
479 	up->uc_state |= state;
480 	netwakeup(up);
481   	if (state == UURGENT)
482 		psignal(up->uc_proc, SIGURG);
483 }
484