Commit | Line | Data |
---|---|---|
e006f425 | 1 | /* tcp_usrreq.c 1.15 81/10/30 */ |
72f24d7d | 2 | |
4eb5d593 | 3 | #include "../h/param.h" |
72f24d7d | 4 | #include "../h/systm.h" |
dad64fdf BJ |
5 | #include "../h/mbuf.h" |
6 | #include "../h/socket.h" | |
7 | #include "../inet/inet.h" | |
8 | #include "../inet/inet_systm.h" | |
9 | #include "../inet/imp.h" | |
10 | #include "../inet/ip.h" | |
11 | #include "../inet/tcp.h" | |
72f24d7d | 12 | #define TCPFSTAB |
186b5a8a BJ |
13 | #ifdef TCPDEBUG |
14 | #define TCPSTATES | |
15 | #endif | |
dad64fdf | 16 | #include "../inet/tcp_fsm.h" |
4eb5d593 | 17 | |
72f24d7d | 18 | tcp_timeo() |
4eb5d593 | 19 | { |
72f24d7d BJ |
20 | register struct tcb *tp; |
21 | int s = splnet(); | |
22 | COUNT(TCP_TIMEO); | |
23 | ||
24 | /* | |
25 | * Search through tcb's and update active timers. | |
26 | */ | |
dad64fdf | 27 | for (tp = tcb_head; tp != NULL; tp = tp->t_tcb_next) { |
72f24d7d BJ |
28 | if (tp->t_init != 0 && --tp->t_init == 0) |
29 | tcp_usrreq(ISTIMER, TINIT, tp, 0); | |
30 | if (tp->t_rexmt != 0 && --tp->t_rexmt == 0) | |
31 | tcp_usrreq(ISTIMER, TREXMT, tp, 0); | |
32 | if (tp->t_rexmttl != 0 && --tp->t_rexmttl == 0) | |
33 | tcp_usrreq(ISTIMER, TREXMTTL, tp, 0); | |
34 | if (tp->t_persist != 0 && --tp->t_persist == 0) | |
35 | tcp_usrreq(ISTIMER, TPERSIST, tp, 0); | |
36 | if (tp->t_finack != 0 && --tp->t_finack == 0) | |
37 | tcp_usrreq(ISTIMER, TFINACK, tp, 0); | |
38 | tp->t_xmt++; | |
39 | } | |
dad64fdf | 40 | tcp_iss += ISSINCR; /* increment iss */ |
72f24d7d BJ |
41 | timeout(tcp_timeo, 0, hz); /* reschedule every second */ |
42 | splx(s); | |
4eb5d593 BJ |
43 | } |
44 | ||
186b5a8a BJ |
45 | tcp_usrreq(input, timertype, tp, mp) |
46 | int input, timertype; | |
72f24d7d | 47 | register struct tcb *tp; |
186b5a8a | 48 | struct mbuf *mp; |
4eb5d593 | 49 | { |
72f24d7d BJ |
50 | int s = splnet(); |
51 | register int nstate; | |
186b5a8a BJ |
52 | #ifdef TCPDEBUG |
53 | struct tcp_debug tdb; | |
54 | #endif | |
72f24d7d BJ |
55 | COUNT(TCP_USRREQ); |
56 | ||
72f24d7d | 57 | nstate = tp->t_state; |
a3d78bbd | 58 | tp->tc_flags &= ~TC_NET_KEEP; |
72f24d7d | 59 | acounts[nstate][input]++; |
186b5a8a BJ |
60 | #ifdef TCPDEBUG |
61 | if ((tp->t_ucb->uc_flags & UDEBUG) || tcpconsdebug) { | |
8f5a3361 | 62 | tdb_setup(tp, (struct th *)0, input, &tdb); |
186b5a8a BJ |
63 | tdb.td_tim = timertype; |
64 | } else | |
65 | tdb.td_tod = 0; | |
66 | #endif | |
72f24d7d BJ |
67 | switch (tcp_fstab[nstate][input]) { |
68 | ||
69 | default: | |
70 | printf("tcp: bad state: tcb=%x state=%d input=%d\n", | |
71 | tp, tp->t_state, input); | |
72 | nstate = EFAILEC; | |
73 | break; | |
4eb5d593 | 74 | |
72f24d7d BJ |
75 | case LIS_CLS: /* 1 */ |
76 | t_open(tp, PASSIVE); | |
77 | nstate = LISTEN; | |
78 | break; | |
4eb5d593 | 79 | |
72f24d7d BJ |
80 | case SYS_CLS: /* 2 */ |
81 | t_open(tp, ACTIVE); | |
82 | send_ctl(tp); | |
83 | nstate = SYN_SENT; | |
84 | break; | |
4eb5d593 | 85 | |
72f24d7d BJ |
86 | case CLS_OPN: /* 10 */ |
87 | t_close(tp, UCLOSED); | |
88 | nstate = CLOSED; | |
89 | break; | |
4eb5d593 | 90 | |
72f24d7d | 91 | case CL2_CLW: /* 10 */ |
a3d78bbd | 92 | tp->tc_flags |= TC_SND_FIN; |
72f24d7d | 93 | send_ctl(tp); |
a3d78bbd | 94 | tp->tc_flags |= TC_USR_CLOSED; |
72f24d7d BJ |
95 | nstate = CLOSING2; |
96 | break; | |
4eb5d593 | 97 | |
72f24d7d | 98 | case TIMERS: /* 14,17,34,35,36,37,38 */ |
186b5a8a | 99 | nstate = tcp_timers(tp, timertype); |
72f24d7d | 100 | break; |
4eb5d593 | 101 | |
72f24d7d BJ |
102 | case CLS_RWT: /* 20 */ |
103 | present_data(tp); | |
104 | if (rcv_empty(tp)) { | |
105 | t_close(tp, UCLOSED); | |
106 | nstate = CLOSED; | |
107 | } else | |
108 | nstate = RCV_WAIT; | |
109 | break; | |
4eb5d593 | 110 | |
72f24d7d | 111 | case FW1_SYR: /* 24,25 */ |
a3d78bbd | 112 | tp->tc_flags |= TC_SND_FIN; |
72f24d7d | 113 | send_ctl(tp); |
a3d78bbd | 114 | tp->tc_flags |= TC_USR_CLOSED; |
72f24d7d BJ |
115 | nstate = FIN_W1; |
116 | break; | |
117 | ||
118 | case SSS_SND: /* 40,41 */ | |
2ad0e257 | 119 | nstate = sss_send(tp, mp); |
72f24d7d BJ |
120 | break; |
121 | ||
122 | case SSS_RCV: /* 42 */ | |
123 | send_ctl(tp); /* send new window */ | |
124 | present_data(tp); | |
125 | break; | |
126 | ||
127 | case CLS_NSY: /* 44 */ | |
128 | t_close(tp, UABORT); | |
129 | nstate = CLOSED; | |
130 | break; | |
131 | ||
132 | case CLS_SYN: /* 45 */ | |
a3d78bbd | 133 | tp->tc_flags |= TC_SND_RST; |
72f24d7d BJ |
134 | send_null(tp); |
135 | t_close(tp, UABORT); | |
136 | nstate = CLOSED; | |
137 | break; | |
138 | ||
139 | case CLS_ACT: /* 47 */ | |
140 | t_close(tp, UNETDWN); | |
141 | nstate = CLOSED; | |
142 | break; | |
143 | ||
144 | case NOP: | |
145 | break; | |
146 | ||
147 | case CLS_ERR: | |
148 | to_user(tp->t_ucb, UCLSERR); | |
149 | break; | |
150 | } | |
72f24d7d | 151 | #ifdef TCPDEBUG |
8f5a3361 BJ |
152 | if (tdb.td_tod) |
153 | tdb_stuff(&tdb, nstate); | |
72f24d7d BJ |
154 | #endif |
155 | /* YECH */ | |
156 | switch (nstate) { | |
157 | ||
186b5a8a | 158 | case CLOSED: |
72f24d7d BJ |
159 | case SAME: |
160 | break; | |
4eb5d593 | 161 | |
72f24d7d | 162 | case EFAILEC: |
186b5a8a BJ |
163 | if (mp) |
164 | m_freem(dtom(mp)); | |
72f24d7d | 165 | break; |
4eb5d593 | 166 | |
72f24d7d BJ |
167 | default: |
168 | tp->t_state = nstate; | |
169 | break; | |
170 | } | |
171 | splx(s); | |
4eb5d593 BJ |
172 | } |
173 | ||
72f24d7d | 174 | t_open(tp, mode) /* set up a tcb for a connection */ |
4eb5d593 | 175 | register struct tcb *tp; |
72f24d7d BJ |
176 | int mode; |
177 | { | |
178 | register struct ucb *up; | |
179 | COUNT(T_OPEN); | |
4eb5d593 | 180 | |
72f24d7d | 181 | /* enqueue the tcb */ |
4eb5d593 | 182 | |
dad64fdf BJ |
183 | if (tcb_head == NULL) { |
184 | tcb_head = tp; | |
185 | tcb_tail = tp; | |
72f24d7d | 186 | } else { |
dad64fdf BJ |
187 | tp->t_tcb_next = tcb_head; |
188 | tcb_head->t_tcb_prev = tp; | |
189 | tcb_head = tp; | |
72f24d7d BJ |
190 | } |
191 | ||
192 | /* initialize non-zero tcb fields */ | |
4eb5d593 | 193 | |
72f24d7d BJ |
194 | tp->t_rcv_next = (struct th *)tp; |
195 | tp->t_rcv_prev = (struct th *)tp; | |
196 | tp->t_xmtime = T_REXMT; | |
197 | tp->snd_end = tp->seq_fin = tp->snd_nxt = tp->snd_hi = | |
dad64fdf | 198 | tp->snd_una = tp->iss = tcp_iss; |
72f24d7d | 199 | tp->snd_off = tp->iss + 1; |
dad64fdf | 200 | tcp_iss += (ISSINCR >> 1) + 1; |
4eb5d593 | 201 | |
72f24d7d BJ |
202 | /* set timeout for open */ |
203 | ||
204 | up = tp->t_ucb; | |
205 | tp->t_init = (up->uc_timeo != 0 ? up->uc_timeo : | |
206 | (mode == ACTIVE ? T_INIT : 0)); | |
207 | up->uc_timeo = 0; /* overlays uc_ssize */ | |
4eb5d593 BJ |
208 | } |
209 | ||
72f24d7d | 210 | t_close(tp, state) |
4eb5d593 | 211 | register struct tcb *tp; |
72f24d7d BJ |
212 | short state; |
213 | { | |
214 | register struct ucb *up; | |
215 | register struct th *t; | |
216 | register struct mbuf *m; | |
72f24d7d | 217 | COUNT(T_CLOSE); |
4eb5d593 | 218 | |
72f24d7d BJ |
219 | up = tp->t_ucb; |
220 | ||
221 | tp->t_init = tp->t_rexmt = tp->t_rexmttl = tp->t_persist = | |
222 | tp->t_finack = 0; | |
223 | ||
224 | /* delete tcb */ | |
225 | ||
226 | if (tp->t_tcb_prev == NULL) | |
dad64fdf | 227 | tcb_head = tp->t_tcb_next; |
72f24d7d BJ |
228 | else |
229 | tp->t_tcb_prev->t_tcb_next = tp->t_tcb_next; | |
230 | if (tp->t_tcb_next == NULL) | |
dad64fdf | 231 | tcb_tail = tp->t_tcb_prev; |
72f24d7d BJ |
232 | else |
233 | tp->t_tcb_next->t_tcb_prev = tp->t_tcb_prev; | |
234 | ||
235 | /* free all data on receive and send buffers */ | |
236 | ||
237 | for (t = tp->t_rcv_next; t != (struct th *)tp; t = t->t_next) | |
238 | m_freem(dtom(t)); | |
239 | ||
240 | if (up->uc_rbuf != NULL) { | |
241 | m_freem(up->uc_rbuf); | |
242 | up->uc_rbuf = NULL; | |
243 | } | |
2de49286 | 244 | up->uc_rcc = 0; |
72f24d7d BJ |
245 | if (up->uc_sbuf != NULL) { |
246 | m_freem(up->uc_sbuf); | |
247 | up->uc_sbuf = NULL; | |
248 | } | |
4bf3e803 | 249 | up->uc_ssize = 0; |
72f24d7d BJ |
250 | for (m = tp->t_rcv_unack; m != NULL; m = m->m_act) { |
251 | m_freem(m); | |
252 | tp->t_rcv_unack = NULL; | |
253 | } | |
dad64fdf BJ |
254 | if (up->uc_template) { |
255 | m_free(dtom(up->uc_template)); | |
256 | up->uc_template = 0; | |
257 | } | |
e006f425 | 258 | wmemfree((caddr_t)tp, 1024); |
72f24d7d BJ |
259 | up->uc_tcb = NULL; |
260 | ||
261 | /* lower buffer allocation and decrement host entry */ | |
262 | ||
dad64fdf BJ |
263 | mbstat.m_lowat -= up->uc_snd + (up->uc_rhiwat/MSIZE) + 2; |
264 | mbstat.m_hiwat = 2 * mbstat.m_lowat; | |
72f24d7d BJ |
265 | if (up->uc_host != NULL) { |
266 | h_free(up->uc_host); | |
267 | up->uc_host = NULL; | |
268 | } | |
269 | ||
270 | /* if user has initiated close (via close call), delete ucb | |
271 | entry, otherwise just wakeup so user can issue close call */ | |
4eb5d593 | 272 | |
a3d78bbd | 273 | if (tp->tc_flags&TC_USR_ABORT) |
72f24d7d BJ |
274 | up->uc_proc = NULL; |
275 | else | |
276 | to_user(up, state); | |
4eb5d593 BJ |
277 | } |
278 | ||
2ad0e257 | 279 | sss_send(tp, m0) |
4eb5d593 | 280 | register struct tcb *tp; |
186b5a8a BJ |
281 | struct mbuf *m0; |
282 | { | |
4eb5d593 | 283 | register struct mbuf *m, *n; |
186b5a8a | 284 | register struct ucb *up = tp->t_ucb; |
4eb5d593 | 285 | register off; |
6e8b2eca | 286 | seq_t last; |
2ad0e257 | 287 | COUNT(SSS_SEND); |
4eb5d593 | 288 | |
4eb5d593 | 289 | last = tp->snd_off; |
186b5a8a | 290 | for (m = n = m0; m != NULL; m = m->m_next) { |
4eb5d593 | 291 | up->uc_ssize++; |
e71a2b4d | 292 | if (m->m_off > MMAXOFF) |
80de89dc | 293 | up->uc_ssize += NMBPG; |
4eb5d593 BJ |
294 | last += m->m_len; |
295 | } | |
80de89dc BJ |
296 | if ((m = up->uc_sbuf) == NULL) |
297 | up->uc_sbuf = n; | |
298 | else { | |
299 | while (m->m_next != NULL) { | |
4eb5d593 BJ |
300 | m = m->m_next; |
301 | last += m->m_len; | |
302 | } | |
e71a2b4d | 303 | if (m->m_off <= MMAXOFF) { |
80de89dc BJ |
304 | last += m->m_len; |
305 | off = m->m_off + m->m_len; | |
e71a2b4d BJ |
306 | while (n && n->m_off <= MMAXOFF && |
307 | (MMAXOFF - off) >= n->m_len) { | |
80de89dc BJ |
308 | bcopy((caddr_t)((int)n + n->m_off), |
309 | (caddr_t)((int)m + off), n->m_len); | |
310 | m->m_len += n->m_len; | |
311 | off += n->m_len; | |
312 | up->uc_ssize--; | |
313 | n = m_free(n); | |
314 | } | |
4eb5d593 BJ |
315 | } |
316 | m->m_next = n; | |
4eb5d593 | 317 | } |
80de89dc BJ |
318 | if (up->uc_flags & UEOL) |
319 | tp->snd_end = last; | |
320 | if (up->uc_flags & UURG) { | |
4eb5d593 | 321 | tp->snd_urp = last+1; |
a3d78bbd | 322 | tp->tc_flags |= TC_SND_URG; |
72f24d7d | 323 | } |
4eb5d593 | 324 | send(tp); |
72f24d7d | 325 | return (SAME); |
4eb5d593 BJ |
326 | } |
327 | ||
186b5a8a BJ |
328 | tcp_timers(tp, timertype) |
329 | register struct tcb *tp; | |
330 | int timertype; | |
4eb5d593 | 331 | { |
4eb5d593 | 332 | |
72f24d7d | 333 | COUNT(TCP_TIMERS); |
186b5a8a | 334 | switch (timertype) { |
4eb5d593 | 335 | |
72f24d7d | 336 | case TINIT: /* initialization timer */ |
a3d78bbd | 337 | if ((tp->tc_flags&TC_SYN_ACKED) == 0) { /* 35 */ |
72f24d7d BJ |
338 | t_close(tp, UINTIMO); |
339 | return (CLOSED); | |
340 | } | |
341 | return (SAME); | |
342 | ||
343 | case TFINACK: /* fin-ack timer */ | |
344 | switch (tp->t_state) { | |
345 | ||
346 | case TIME_WAIT: | |
347 | /* | |
348 | * We can be sure our ACK of foreign FIN was rcvd, | |
349 | * and can close if no data left for user. | |
350 | */ | |
351 | if (rcv_empty(tp)) { | |
352 | t_close(tp, UCLOSED); /* 14 */ | |
353 | return (CLOSED); | |
354 | } | |
355 | return (RCV_WAIT); /* 17 */ | |
356 | ||
357 | case CLOSING1: | |
a3d78bbd | 358 | tp->tc_flags |= TC_WAITED_2_ML; |
72f24d7d BJ |
359 | return (SAME); |
360 | ||
361 | default: | |
362 | return (SAME); | |
363 | } | |
4eb5d593 | 364 | |
72f24d7d BJ |
365 | case TREXMT: /* retransmission timer */ |
366 | if (tp->t_rexmt_val > tp->snd_una) { /* 34 */ | |
367 | /* | |
368 | * Set up for a retransmission, increase rexmt time | |
369 | * in case of multiple retransmissions. | |
370 | */ | |
371 | tp->snd_nxt = tp->snd_una; | |
a3d78bbd | 372 | tp->tc_flags |= TC_REXMT; |
72f24d7d BJ |
373 | tp->t_xmtime = tp->t_xmtime << 1; |
374 | if (tp->t_xmtime > T_REMAX) | |
375 | tp->t_xmtime = T_REMAX; | |
376 | send(tp); | |
377 | } | |
378 | return (SAME); | |
379 | ||
380 | case TREXMTTL: /* retransmit too long */ | |
381 | if (tp->t_rtl_val > tp->snd_una) /* 36 */ | |
382 | to_user(tp->t_ucb, URXTIMO); | |
383 | /* | |
384 | * If user has already closed, abort the connection. | |
385 | */ | |
a3d78bbd | 386 | if (tp->tc_flags & TC_USR_CLOSED) { |
72f24d7d BJ |
387 | t_close(tp, URXTIMO); |
388 | return (CLOSED); | |
389 | } | |
390 | return (SAME); | |
391 | ||
392 | case TPERSIST: /* persist timer */ | |
393 | /* | |
394 | * Force a byte send through closed window. | |
395 | */ | |
a3d78bbd | 396 | tp->tc_flags |= TC_FORCE_ONE; |
72f24d7d BJ |
397 | send(tp); |
398 | return (SAME); | |
399 | } | |
400 | panic("tcp_timers"); | |
4eb5d593 BJ |
401 | } |
402 | ||
72f24d7d BJ |
403 | /* THIS ROUTINE IS A CROCK */ |
404 | to_user(up, state) | |
405 | register struct ucb *up; | |
406 | register short state; | |
4eb5d593 | 407 | { |
72f24d7d | 408 | COUNT(TO_USER); |
4eb5d593 | 409 | |
72f24d7d BJ |
410 | up->uc_state |= state; |
411 | netwakeup(up); | |
412 | if (state == UURGENT) | |
413 | psignal(up->uc_proc, SIGURG); | |
4eb5d593 | 414 | } |
186b5a8a BJ |
415 | |
416 | #ifdef TCPDEBUG | |
417 | tcp_prt(tdp) | |
418 | register struct tcp_debug *tdp; | |
419 | { | |
420 | COUNT(TCP_PRT); | |
421 | ||
8f5a3361 | 422 | printf("TCP(%x) %s x %s", |
186b5a8a BJ |
423 | tdp->td_tcb, tcpstates[tdp->td_old], tcpinputs[tdp->td_inp]); |
424 | if (tdp->td_inp == ISTIMER) | |
425 | printf("(%s)", tcptimers[tdp->td_tim]); | |
426 | printf(" --> %s", | |
427 | tcpstates[(tdp->td_new > 0) ? tdp->td_new : tdp->td_old]); | |
428 | /* GROSS... DEPENDS ON SIGN EXTENSION OF CHARACTERS */ | |
429 | if (tdp->td_new < 0) | |
430 | printf(" (FAILED)"); | |
8f5a3361 BJ |
431 | if (tdp->td_sno) { |
432 | printf(" sno %x ano %x win %d len %d flags %x", | |
433 | tdp->td_sno, tdp->td_ano, tdp->td_wno, tdp->td_lno, tdp->td_flg); | |
434 | } | |
186b5a8a BJ |
435 | printf("\n"); |
436 | } | |
437 | #endif | |
e006f425 BJ |
438 | #ifdef TCPDEBUG |
439 | tdb_setup(tp, n, input, tdp) | |
440 | struct tcb *tp; | |
441 | register struct th *n; | |
442 | int input; | |
443 | register struct tcp_debug *tdp; | |
444 | { | |
445 | ||
446 | tdp->td_tod = time; | |
447 | tdp->td_tcb = tp; | |
448 | tdp->td_old = tp->t_state; | |
449 | tdp->td_inp = input; | |
450 | tdp->td_tim = 0; | |
451 | tdp->td_new = -1; | |
452 | if (n) { | |
453 | tdp->td_sno = n->t_seq; | |
454 | tdp->td_ano = n->t_ackno; | |
455 | tdp->td_wno = n->t_win; | |
456 | tdp->td_lno = n->t_len; | |
457 | tdp->td_flg = n->th_flags; | |
458 | } else | |
459 | tdp->td_sno = tdp->td_ano = tdp->td_wno = tdp->td_lno = | |
460 | tdp->td_flg = 0; | |
461 | } | |
462 | ||
463 | tdb_stuff(tdp, nstate) | |
464 | struct tcp_debug *tdp; | |
465 | int nstate; | |
466 | { | |
467 | ||
468 | tdp->td_new = nstate; | |
469 | tcp_debug[tdbx++ % TDBSIZE] = *tdp; | |
470 | if (tcpconsdebug & 2) | |
471 | tcp_prt(tdp); | |
472 | } | |
473 | #endif |