Commit | Line | Data |
---|---|---|
17efd7fe MK |
1 | #ifdef RCSIDENT |
2 | static char rcsident[] = "$Header: tcp_states.c,v 1.21 85/07/31 09:42:53 walsh Exp $"; | |
3 | #endif | |
4 | ||
5 | ||
6 | #include "../h/param.h" | |
7 | #include "../h/mbuf.h" | |
8 | #include "../h/socket.h" | |
9 | #include "../h/socketvar.h" | |
10 | #include "../h/errno.h" | |
11 | #include "../h/systm.h" | |
12 | ||
13 | #include "../net/if.h" | |
14 | #include "../net/route.h" | |
15 | ||
16 | #include "../bbnnet/in.h" | |
17 | #include "../bbnnet/in_pcb.h" | |
18 | #include "../bbnnet/in_var.h" | |
19 | #include "../bbnnet/net.h" | |
20 | #include "../bbnnet/fsm.h" | |
21 | #include "../bbnnet/tcp.h" | |
22 | #include "../bbnnet/seq.h" | |
23 | #include "../bbnnet/ip.h" | |
24 | #include "../bbnnet/macros.h" | |
25 | #include "../bbnnet/sws.h" | |
26 | #ifdef HMPTRAPS | |
27 | #include "../bbnnet/hmp_traps.h" | |
28 | #endif | |
29 | ||
30 | extern struct rtentry *ip_route(); | |
31 | ||
32 | /* | |
33 | * These are the action routines of the TCP finite state machine. They are | |
34 | * called from the TCP fsm dispatcher action(). These routines call | |
35 | * on the routines in tcp_procs.c to do the actual segment processing. | |
36 | */ | |
37 | ||
38 | /* | |
39 | * UNOPENED x IUOPENA == passive open == listen() | |
40 | */ | |
41 | lis_cls(wp) | |
42 | struct work *wp; | |
43 | { | |
44 | register struct tcpcb *tp = wp->w_tcb; | |
45 | ||
46 | /* listen() system call */ | |
47 | ||
48 | /* | |
49 | * Don't know who we're talking to yet, so we don't have a route | |
50 | * or mtu yet. | |
51 | */ | |
52 | tp->t_maxseg = TCPMAXSND; | |
53 | tp->t_timers[TINIT] = tp->t_itimeo; | |
54 | ||
55 | return(LISTEN); | |
56 | } | |
57 | ||
58 | /* | |
59 | * UNOPENED x IUOPENR == active open == connect() | |
60 | */ | |
61 | sys_cls(wp) | |
62 | register struct work *wp; | |
63 | { | |
64 | register struct tcpcb *tp; | |
65 | register struct inpcb *inp; | |
66 | ||
67 | /* connect() system call */ | |
68 | ||
69 | tp = wp->w_tcb; | |
70 | inp = tp->t_in_pcb; | |
71 | /* | |
72 | * Know foreign host and have a route to there. | |
73 | */ | |
74 | #ifdef NOTCPOPTS | |
75 | tp->t_maxseg = inp->inp_route.ro_rt->rt_ifp->if_mtu - TCPIPMAX; | |
76 | #else | |
77 | /* | |
78 | * Best can do until other guy tells us otherwise. | |
79 | */ | |
80 | tp->t_maxseg = | |
81 | MIN(inp->inp_route.ro_rt->rt_ifp->if_mtu - TCPIPMAX, TCPMAXSND); | |
82 | #endif | |
83 | tp->t_maxseg -= inp->inp_optlen; | |
84 | ||
85 | tp->t_timers[TINIT] = (tp->t_itimeo ? tp->t_itimeo : TCP_tvINIT); | |
86 | ||
87 | send_tcp(tp, TCP_CTL); /* send SYN */ | |
88 | return(SYN_SENT); | |
89 | } | |
90 | ||
91 | /* | |
92 | * UNOPENED x IUCLOSE | |
93 | * LISTEN x IUCLOSE | |
94 | * SYN_SENT x IUCLOSE | |
95 | * | |
96 | * User close request before receiving foreign SYN | |
97 | */ | |
98 | cls_opn(wp) | |
99 | struct work *wp; | |
100 | { | |
101 | t_close(wp->w_tcb, ECONNABORTED); | |
102 | return(CLOSED); | |
103 | } | |
104 | ||
105 | /* | |
106 | * SYN_RCVD x IUCLOSE | |
107 | * L_SYN_RCVD x IUCLOSE | |
108 | * ESTAB x IUCLOSE | |
109 | * | |
110 | * close request on synched connection | |
111 | */ | |
112 | fw1_syr(wp) | |
113 | struct work *wp; | |
114 | { | |
115 | register struct tcpcb *tp = wp->w_tcb; | |
116 | ||
117 | tp->snd_fin = TRUE; /* send FIN */ | |
118 | send_tcp(tp, TCP_CTL); | |
119 | tp->usr_closed = TRUE; | |
120 | tp->t_noact = TCP_tvNOACT; | |
121 | tp->t_timers[TNOACT] = TCP_tvNOACT; | |
122 | return(FIN_W1); | |
123 | } | |
124 | ||
125 | /* | |
126 | * CLOSE_WAIT x IUCLOSE | |
127 | * | |
128 | * close request after received foreign FIN | |
129 | */ | |
130 | cl2_clw(wp) | |
131 | struct work *wp; | |
132 | { | |
133 | register struct tcpcb *tp = wp->w_tcb; | |
134 | ||
135 | tp->snd_fin = TRUE; /* send our own FIN */ | |
136 | send_tcp(tp, TCP_CTL); | |
137 | tp->usr_closed = TRUE; | |
138 | tp->t_noact = TCP_tvNOACT; | |
139 | tp->t_timers[TNOACT] = TCP_tvNOACT; | |
140 | return(CLOSING2); | |
141 | } | |
142 | ||
143 | /* | |
144 | * UNOPENED x IUABORT | |
145 | * LISTEN x IUABORT | |
146 | * SYN_SENT x IUABORT | |
147 | * SYN_RCVD x IUABORT | |
148 | * L_SYN_RCVD x IUABORT | |
149 | * | |
150 | * User abort request on unsynched connection | |
151 | */ | |
152 | cls_nsy(wp) | |
153 | struct work *wp; | |
154 | { | |
155 | t_close(wp->w_tcb, ECONNABORTED); | |
156 | return(CLOSED); | |
157 | } | |
158 | ||
159 | /* | |
160 | * ESTAB x IUABORT | |
161 | * FIN_WAIT_1 x IUABORT | |
162 | * FIN_WAIT_2 x IUABORT | |
163 | * TIME_WAIT x IUABORT | |
164 | * CLOSE_WAIT x IUABORT | |
165 | * CLOSING_1 x IUABORT | |
166 | * CLOSING_2 x IUABORT | |
167 | * RCV_WAIT x IUABORT | |
168 | * | |
169 | * User abort request on synched connection | |
170 | */ | |
171 | cls_syn(wp) | |
172 | struct work *wp; | |
173 | { | |
174 | register struct tcpcb *tp = wp->w_tcb; | |
175 | ||
176 | tp->snd_rst = TRUE; /* send reset */ | |
177 | (void) send_pkt(tp, 0, 0); | |
178 | /* tp->ack_due = FALSE; don't since about to throw tcpcb away */ | |
179 | t_close(tp, ECONNABORTED); | |
180 | return(CLOSED); | |
181 | } | |
182 | ||
183 | /* | |
184 | * LISTEN x INRECV | |
185 | * | |
186 | * From tcp_input/netprepr, we know the packet is a well formed SYN | |
187 | */ | |
188 | lis_netr(wp) | |
189 | struct work *wp; | |
190 | { | |
191 | register struct tcpcb *tp; | |
192 | register struct th *n; | |
193 | register struct inpcb *inp; | |
194 | register struct socket *so, *newso; | |
195 | struct rtentry *rt; | |
196 | struct tcpcb *newtp; | |
197 | struct inpcb *newinp; | |
198 | ||
199 | struct in_addr firsthop; | |
200 | extern int ip_nhops; | |
201 | extern struct in_addr ip_hops[]; | |
202 | ||
203 | n = (struct th *)wp->w_dat; | |
204 | ||
205 | /* | |
206 | * Need to route on basis of IP destination -- see ip_send() | |
207 | * ### What if loose routing and 1st hop not on local net and reroute? | |
208 | */ | |
209 | if (ip_nhops == 0) | |
210 | firsthop = n->t_s; | |
211 | else | |
212 | /* source routed SYN packet */ | |
213 | firsthop = ip_hops[ip_nhops]; | |
214 | ||
215 | /* | |
216 | * O.k., let's get a route back to him | |
217 | */ | |
218 | if (!(rt = ip_route(&n->t_d, &firsthop))) | |
219 | { | |
220 | /* | |
221 | * Can't talk to him. Leave socket in receive state | |
222 | * so we can connect to someone else, since we haven't | |
223 | * been committed to anything yet anyway. | |
224 | * ### Drop his info on the floor. | |
225 | * Let the other machine just figure out on it's own that | |
226 | * it can't reach us that way. | |
227 | */ | |
228 | no_route ("tcp", n->t_d, firsthop); | |
229 | return(LISTEN); | |
230 | } | |
231 | ||
232 | tp = wp->w_tcb; | |
233 | inp = tp->t_in_pcb; | |
234 | so = inp->inp_socket; | |
235 | ||
236 | /* | |
237 | * This socket is in the listen state, so the socket should have | |
238 | * so_options & SO_ACCEPTCONN set (solisten()). | |
239 | * | |
240 | * The order of sonewconn() and soisconnected() is | |
241 | * important, in order for the process to be woken up | |
242 | * at a time when the sleep condition is fulfilled. | |
243 | * sonewconn() is done here on the original socket, and | |
244 | * soisconnected() is done later in syr_netr() on the new | |
245 | * socket. | |
246 | */ | |
247 | if (newso = sonewconn(so)) | |
248 | { | |
249 | newinp = (struct inpcb *) newso->so_pcb; | |
250 | newtp = (struct tcpcb *) newinp->inp_ppcb; | |
251 | /* | |
252 | * Remember our peer for this connection. | |
253 | */ | |
254 | newinp->inp_faddr = n->t_s; | |
255 | newinp->inp_fport = n->t_src; | |
256 | newinp->inp_laddr = n->t_d; | |
257 | if (ip_nhops > 0) | |
258 | { | |
259 | /* | |
260 | * optlen includes the source route to be copied | |
261 | * to the outgoing IP header, not the firsthop | |
262 | * which replaces ip_dst. | |
263 | */ | |
264 | bcopy((caddr_t)ip_hops,newinp->inp_options, (unsigned)(ip_nhops+1)*4); | |
265 | newinp->inp_optlen = ip_nhops * 4; | |
266 | } | |
267 | /* | |
268 | * and copy fields into the new inpcb | |
269 | */ | |
270 | newinp->inp_lport = inp->inp_lport; | |
271 | newinp->inp_route.ro_rt = rt; | |
272 | ||
273 | /* | |
274 | * and copy fields to the new tcpcb | |
275 | */ | |
276 | newtp->t_maxfrag = tp->t_maxfrag; /* set in tcp_input() */ | |
277 | newtp->t_itimeo = tp->t_itimeo; | |
278 | newtp->t_noact = tp->t_noact; | |
279 | newtp->t_push = tp->t_push; | |
280 | newtp->t_noactsig = tp->t_noactsig; | |
281 | newtp->t_noactprobe = tp->t_noactprobe; | |
282 | ||
283 | /* | |
284 | * and initialize others with new info | |
285 | * Upward negotiation of t_maxseg in tcp_opt() done | |
286 | * on socket in LISTEN. | |
287 | */ | |
288 | newtp->t_maxseg = MIN(rt->rt_ifp->if_mtu - TCPIPMAX, tp->t_maxseg); | |
289 | newtp->t_maxseg -= newinp->inp_optlen; | |
290 | /* | |
291 | * In case next client doesn't negotiate maxseg. | |
292 | */ | |
293 | tp->t_maxseg = TCPMAXSND; | |
294 | ||
295 | ||
296 | if (!(newtp->t_template = tcp_template(newtp))) | |
297 | { | |
298 | soabort (newso); | |
299 | return (LISTEN); | |
300 | } | |
301 | ||
302 | newtp->sws_qff = SWS_QFF_DEF; | |
303 | ||
304 | /* | |
305 | * So can debug connection problems without having to change | |
306 | * every program or apply debugging flag to each program every | |
307 | * time run it. | |
308 | */ | |
309 | dowedebug(newinp, newso, &tcp_dfilter); | |
310 | ||
311 | /* | |
312 | * rcv_tcp may set fin_rcvd. If so, We went up and down or | |
313 | * we got a garbage/misrouted packet. If it's set, it's | |
314 | * meant for some other socket or some other instantiation | |
315 | * of it. In any case, ignore it and listen for other | |
316 | * talkers. | |
317 | */ | |
318 | rcv_tcp(newtp, n, TCP_DATA); | |
319 | ||
320 | if (newtp->fin_rcvd) | |
321 | soabort (newso); | |
322 | else | |
323 | { | |
324 | /* | |
325 | * no FIN (4) | |
326 | * start init timer now that we have foreign host. | |
327 | * Parent socket might have init timer as zero to | |
328 | * avoid getting ETIMEDOUT, but we do want this | |
329 | * child socket to time out on synchronization | |
330 | * just in case other host just went down. | |
331 | */ | |
332 | newtp->t_timers[TINIT] = (newtp->t_itimeo != 0 | |
333 | ? newtp->t_itimeo | |
334 | : TCP_tvINIT/2); | |
335 | newtp->t_state = L_SYN_RCVD; | |
336 | } | |
337 | } | |
338 | else | |
339 | rtfree(rt); | |
340 | ||
341 | return(LISTEN); /* original file descriptor stays in LISTEN state */ | |
342 | } | |
343 | ||
344 | /* | |
345 | * SYN_SENT x INRECV | |
346 | * | |
347 | * from tcp_input/netprepr, we know its a SYN, with perhaps a well formed ACK | |
348 | */ | |
349 | sys_netr(wp) | |
350 | struct work *wp; | |
351 | { | |
352 | register struct tcpcb *tp = wp->w_tcb; | |
353 | register struct th *n = (struct th *)wp->w_dat; | |
354 | ||
355 | rcv_tcp(tp, n, TCP_DATA); | |
356 | if (tp->fin_rcvd) | |
357 | { /* got a FIN */ | |
358 | ||
359 | /* if good ACK, present any data */ | |
360 | ||
361 | if (n->t_flags&T_ACK) | |
362 | { | |
363 | if (SEQ_GT(n->t_ackno, tp->iss)) /* 32 */ | |
364 | present_data(tp); | |
365 | } | |
366 | else | |
367 | { /* 9 */ | |
368 | tp->t_timers[TFINACK] = TCP_tv2ML; | |
369 | tp->waited_2_ml = FALSE; | |
370 | } | |
371 | tp->t_timers[TNOACT] = tp->t_noact; | |
372 | return (CLOSE_WAIT); | |
373 | } | |
374 | else /* no FIN */ | |
375 | /* if good ACK, open connection, otherwise wait for one */ | |
376 | if (n->t_flags&T_ACK) | |
377 | { /* 11 */ | |
378 | present_data(tp); | |
379 | tp->t_timers[TNOACT] = tp->t_noact; | |
380 | soisconnected (tp->t_in_pcb->inp_socket); | |
381 | return(ESTAB); | |
382 | } | |
383 | ||
384 | return(SYN_RCVD); /* 8 */ | |
385 | } | |
386 | ||
387 | /* | |
388 | * SYN_RCVD x INRECV | |
389 | * L_SYN_RCVD x INRECV | |
390 | * | |
391 | * from tcp_input/netprepr, we know its an ACK of our SYN | |
392 | */ | |
393 | syr_netr(wp) | |
394 | struct work *wp; | |
395 | { | |
396 | register struct tcpcb *tp = wp->w_tcb; | |
397 | register struct th *n = (struct th *)wp->w_dat; | |
398 | ||
399 | rcv_tcp(tp, n, TCP_DATA); | |
400 | present_data(tp); | |
401 | ||
402 | /* if no FIN, open connection, otherwise wait for user close */ | |
403 | ||
404 | tp->t_timers[TNOACT] = tp->t_noact; | |
405 | if (tp->fin_rcvd) /* 33 */ | |
406 | return(CLOSE_WAIT); | |
407 | else | |
408 | { | |
409 | /* 5 */ | |
410 | soisconnected (tp->t_in_pcb->inp_socket); | |
411 | return(ESTAB); | |
412 | } | |
413 | } | |
414 | ||
415 | /* | |
416 | * ESTAB x INRECV | |
417 | */ | |
418 | est_netr(wp) | |
419 | struct work *wp; | |
420 | { | |
421 | register struct tcpcb *tp = wp->w_tcb; | |
422 | ||
423 | rcv_tcp(tp, (struct th *)wp->w_dat, TCP_DATA); | |
424 | PRESENT_DATA(tp); | |
425 | ||
426 | /* if no FIN, remain open, otherwise wait for user close */ | |
427 | ||
428 | if (tp->fin_rcvd) /* 12 */ | |
429 | return(CLOSE_WAIT); | |
430 | else /* 39 */ | |
431 | return(SAME); | |
432 | } | |
433 | ||
434 | /* | |
435 | * FIN_WAIT_1 x INRECV | |
436 | * | |
437 | * incoming segment after user has closed | |
438 | */ | |
439 | fw1_netr(wp) | |
440 | struct work *wp; | |
441 | { | |
442 | register struct tcpcb *tp = wp->w_tcb; | |
443 | register struct th *n = (struct th *)wp->w_dat; | |
444 | ||
445 | /* process any incoming data, since we closed but they didn't */ | |
446 | ||
447 | rcv_tcp(tp, n, TCP_DATA); | |
448 | present_data(tp); | |
449 | ||
450 | /* send any data remaining on send buffer */ | |
451 | ||
452 | send_tcp(tp, TCP_DATA); | |
453 | if (ack_fin(tp, n)) | |
454 | { /* our FIN got ACKed */ | |
455 | if (tp->fin_rcvd) | |
456 | { /* got for FIN (28) */ | |
457 | tp->t_timers[TFINACK] = TCP_tv2ML; | |
458 | tp->waited_2_ml = FALSE; | |
459 | return(TIME_WAIT); | |
460 | } | |
461 | else /* no FIN, wait (27) */ | |
462 | return(FIN_W2); | |
463 | } | |
464 | else | |
465 | { /* no ACK of FIN */ | |
466 | if (tp->fin_rcvd) | |
467 | { /* got for FIN (26) */ | |
468 | tp->t_timers[TFINACK] = TCP_tv2ML; | |
469 | tp->waited_2_ml = FALSE; | |
470 | return(CLOSING1); | |
471 | } | |
472 | } | |
473 | return(SAME); /* 39 */ | |
474 | } | |
475 | ||
476 | /* | |
477 | * FIN_WAIT_2 x INRECV | |
478 | * | |
479 | * incoming segment while waiting for foreign FIN | |
480 | */ | |
481 | fw2_netr(wp) | |
482 | struct work *wp; | |
483 | { | |
484 | register struct tcpcb *tp = wp->w_tcb; | |
485 | register struct th *n = (struct th *)wp->w_dat; | |
486 | ||
487 | /* process data since we closed, but they may not have */ | |
488 | ||
489 | rcv_tcp(tp, n, TCP_DATA); | |
490 | present_data(tp); | |
491 | ||
492 | /* if we get the FIN, start the finack timer, else keep waiting */ | |
493 | ||
494 | if (tp->fin_rcvd) | |
495 | { /* got for FIN (29) */ | |
496 | tp->t_timers[TFINACK] = TCP_tv2ML; | |
497 | tp->waited_2_ml = FALSE; | |
498 | return(TIME_WAIT); | |
499 | } | |
500 | else /* 39 */ | |
501 | return(SAME); | |
502 | } | |
503 | ||
504 | /* | |
505 | * TIME_WAIT x INRECV | |
506 | * | |
507 | * The close protocol (exchange of FINs) has progressed as far as it can. | |
508 | * We do not enter CLOSED immediately, but use TIME_WAIT so that if our ack | |
509 | * of other guys FIN didn't reach him, he can retransmit and we'll ack his | |
510 | * fin rather than respond with an rst. | |
511 | * | |
512 | * Since we received a packet, apparently our ack of his fin didn't get | |
513 | * there and we'll have to try again. Restart finack timer in case this | |
514 | * one fails too. | |
515 | */ | |
516 | sss_syn(wp) | |
517 | struct work *wp; | |
518 | { | |
519 | register struct tcpcb *tp = wp->w_tcb; | |
520 | ||
521 | rcv_tcp(tp, (struct th *) wp->w_dat, TCP_DATA); | |
522 | present_data(tp); | |
523 | tp->t_timers[TFINACK] = TCP_tv2ML; | |
524 | return(SAME); | |
525 | } | |
526 | ||
527 | /* | |
528 | * CLOSE_WAIT x INRECV | |
529 | * | |
530 | * incoming segment after receipt of foreign FIN (local end still open) | |
531 | */ | |
532 | cwt_netr(wp) | |
533 | struct work *wp; | |
534 | { | |
535 | register struct tcpcb *tp = wp->w_tcb; | |
536 | register struct th *n = (struct th *)wp->w_dat; | |
537 | ||
538 | /* either duplicate FIN or data */ | |
539 | ||
540 | if (n->t_flags&T_FIN) | |
541 | { | |
542 | if (n->t_flags&T_ACK && SEQ_LEQ(n->t_ackno, tp->seq_fin)) | |
543 | { | |
544 | rcv_tcp(tp, n, TCP_CTL); | |
545 | tp->t_timers[TFINACK] = TCP_tv2ML; | |
546 | tp->waited_2_ml = FALSE; | |
547 | } | |
548 | else /* 31 */ | |
549 | send_tcp(tp, TCP_CTL); | |
550 | } | |
551 | else | |
552 | { /* duplicate data (39) */ | |
553 | rcv_tcp(tp, n, TCP_DATA); | |
554 | present_data(tp); | |
555 | } | |
556 | return(SAME); | |
557 | } | |
558 | ||
559 | /* | |
560 | * CLOSING_1 x INRECV | |
561 | * | |
562 | * incoming segment after we closed | |
563 | */ | |
564 | cl1_netr(wp) | |
565 | struct work *wp; | |
566 | { | |
567 | register struct tcpcb *tp = wp->w_tcb; | |
568 | register struct th *n = (struct th *)wp->w_dat; | |
569 | ||
570 | if (ack_fin(tp, n)) | |
571 | { /* got ACK of our FIN */ | |
572 | if (n->t_flags&T_FIN) | |
573 | { /* got for FIN (23) */ | |
574 | rcv_tcp(tp, n, TCP_CTL); | |
575 | tp->t_timers[TFINACK] = TCP_tv2ML; | |
576 | tp->waited_2_ml = FALSE; | |
577 | return(TIME_WAIT); | |
578 | } | |
579 | else | |
580 | { | |
581 | ||
582 | /* if wait done, see if any data left for user */ | |
583 | ||
584 | if (tp->waited_2_ml) | |
585 | if (rcv_empty(tp)) | |
586 | { /* 15 */ | |
587 | t_close(tp, ECONNABORTED); | |
588 | return(CLOSED); | |
589 | } | |
590 | else | |
591 | return(RCV_WAIT); /* 18 */ | |
592 | else | |
593 | return(TIME_WAIT); /* 22 */ | |
594 | } | |
595 | } | |
596 | else | |
597 | { /* our FIN not ACKed yet */ | |
598 | if (n->t_flags&T_FIN) | |
599 | { /* rcvd for FIN (30) */ | |
600 | rcv_tcp(tp, n, TCP_CTL); | |
601 | tp->t_timers[TFINACK] = TCP_tv2ML; | |
602 | tp->waited_2_ml = FALSE; | |
603 | } | |
604 | else | |
605 | { /* no FIN, just proc new data (39) */ | |
606 | rcv_tcp(tp, n, TCP_DATA); | |
607 | present_data(tp); | |
608 | } | |
609 | } | |
610 | return(SAME); | |
611 | } | |
612 | ||
613 | /* | |
614 | * CLOSING_2 x INRECV | |
615 | * | |
616 | * incoming segment after both of us have started closing | |
617 | */ | |
618 | cl2_netr(wp) | |
619 | struct work *wp; | |
620 | { | |
621 | register struct tcpcb *tp = wp->w_tcb; | |
622 | register struct th *n = (struct th *)wp->w_dat; | |
623 | ||
624 | if (ack_fin(tp, n)) | |
625 | { /* this is ACK of our fin */ | |
626 | ||
627 | /* if no data left for user, close; otherwise wait */ | |
628 | ||
629 | if (rcv_empty(tp)) | |
630 | { /* 16 */ | |
631 | t_close(tp, ECONNABORTED); | |
632 | return(CLOSED); | |
633 | } | |
634 | else /* 19 */ | |
635 | return(RCV_WAIT); | |
636 | } | |
637 | else | |
638 | { /* no ACK of our FIN */ | |
639 | /* duplicate FIN or data */ | |
640 | ||
641 | if (n->t_flags&T_FIN) /* 31 */ | |
642 | send_tcp(tp, TCP_CTL); /* ACK duplicate FIN */ | |
643 | else | |
644 | { | |
645 | /* 39 */ | |
646 | rcv_tcp(tp, n, TCP_DATA); | |
647 | present_data(tp); | |
648 | } | |
649 | } | |
650 | return(SAME); | |
651 | } | |
652 | ||
653 | /* | |
654 | * RCV_WAIT x INRECV | |
655 | */ | |
656 | rwt_netr(wp) /* incoming seg while waiting for user rcv (30,21) */ | |
657 | struct work *wp; | |
658 | { | |
659 | register struct tcpcb *tp = wp->w_tcb; | |
660 | register struct th *n = (struct th *)wp->w_dat; | |
661 | ||
662 | /* handle duplicate ACK of our FIN */ | |
663 | ||
664 | if (n->t_flags&T_FIN && n->t_flags&T_ACK && SEQ_LEQ(n->t_ackno, tp->seq_fin)) | |
665 | { /* 30 */ | |
666 | rcv_tcp(tp, n, TCP_CTL); | |
667 | tp->t_timers[TFINACK] = TCP_tv2ML; | |
668 | tp->waited_2_ml = FALSE; | |
669 | } | |
670 | return(SAME); | |
671 | } | |
672 | ||
673 | /* | |
674 | * ESTAB x IURECV | |
675 | * CLOSE_WAIT x IURECV | |
676 | * | |
677 | * and allowing for shutdown() | |
678 | * | |
679 | * FIN_WAIT_1 x IURECV | |
680 | * FIN_WAIT_2 x IURECV | |
681 | * TIME_WAIT x IURECV | |
682 | * CLOSING_1 x IURECV | |
683 | * CLOSING_2 x IURECV | |
684 | */ | |
685 | sss_rcv(wp) /* rcv request on open connection (42) */ | |
686 | struct work *wp; | |
687 | { | |
688 | register struct tcpcb *tp = wp->w_tcb; | |
689 | ||
690 | PRESENT_DATA(tp); | |
691 | ||
692 | /* if last window sent was zero, send an ACK to update window */ | |
693 | ||
694 | if (tp->sent_zero) | |
695 | { | |
696 | tp->force_ack = TRUE; /* don't delay ACK here */ | |
697 | send_tcp(tp, TCP_CTL); | |
698 | } | |
699 | return(SAME); | |
700 | } | |
701 | ||
702 | /* | |
703 | * RCV_WAIT x IURECV | |
704 | */ | |
705 | cls_rwt(wp) /* rcv request after foreign close (20) */ | |
706 | struct work *wp; | |
707 | { | |
708 | register struct tcpcb *tp = wp->w_tcb; | |
709 | ||
710 | present_data(tp); /* present any remaining data */ | |
711 | if (rcv_empty(tp)) | |
712 | { | |
713 | t_close(tp, ECONNABORTED); | |
714 | return(CLOSED); | |
715 | } | |
716 | else | |
717 | return(RCV_WAIT); | |
718 | } | |
719 | ||
720 | /* | |
721 | * SYN_SENT x IUSEND | |
722 | * SYN_RCVD x IUSEND | |
723 | * ESTAB x IUSEND | |
724 | * CLOSE_WAIT x IUSEND | |
725 | * | |
726 | * For SYN_SENT and SYN_RCVD, just want to buffer data until connected. | |
727 | */ | |
728 | sss_snd(wp) /* send request on open connection (40,41) */ | |
729 | struct work *wp; | |
730 | { | |
731 | register struct tcpcb *tcp = wp->w_tcb; | |
732 | register struct inpcb *inp = tcp->t_in_pcb; | |
733 | sequence last; | |
734 | ||
735 | sbappend(&inp->inp_socket->so_snd, (struct mbuf *) wp->w_dat); | |
736 | last = tcp->snd_una + inp->inp_socket->so_snd.sb_cc; | |
737 | ||
738 | if (tcp->t_push) | |
739 | tcp->snd_end = last; | |
740 | if (tcp->t_urg) | |
741 | { | |
742 | tcp->snd_urp = last; /* this byte is not urgent */ | |
743 | tcp->snd_urg = TRUE; | |
744 | } | |
745 | send_tcp(tcp, TCP_DATA); | |
746 | return(SAME); | |
747 | } | |
748 | ||
749 | cls_act(wp) /* net closing open connection (47) */ | |
750 | struct work *wp; | |
751 | { | |
752 | t_close(wp->w_tcb, ECONNABORTED); | |
753 | return(CLOSED); | |
754 | } | |
755 | ||
756 | cls_err(wp) /* invalid user request in closing states */ | |
757 | struct work *wp; | |
758 | { | |
759 | advise_user(tcpcbtoso(wp->w_tcb), ECONNABORTED); | |
760 | return(SAME); | |
761 | } | |
762 | ||
763 | ||
764 | ||
765 | timers(wp) /* timer processor (14,17,34,35,36,37,38) */ | |
766 | struct work *wp; | |
767 | { | |
768 | register struct tcpcb *tp = wp->w_tcb; | |
769 | register type = wp->w_stype; | |
770 | ||
771 | switch (type) | |
772 | { | |
773 | ||
774 | case TINIT: /* initialization timer */ | |
775 | /* | |
776 | * Haven't got an ACK of our SYN yet | |
777 | */ | |
778 | if (tp->t_in_pcb->inp_socket->so_state & SS_NOFDREF) | |
779 | { | |
780 | /* | |
781 | * was a child socket of a listen(2)er trying to | |
782 | * establish connection with other end. | |
783 | * (state L_SYN_RCVD) | |
784 | */ | |
785 | t_close(tp, ETIMEDOUT); | |
786 | return(CLOSED); | |
787 | } | |
788 | /* socket in connect(2) */ | |
789 | advise_user(tcpcbtoso(tp), ETIMEDOUT); | |
790 | tp->t_timers[TINIT] = tp->t_itimeo; | |
791 | break; | |
792 | ||
793 | case TFINACK: /* fin-ack timer */ | |
794 | ||
795 | if (tp->t_state == TIME_WAIT) | |
796 | { | |
797 | ||
798 | /* can be sure our ACK of for FIN was rcvd, | |
799 | can close if no data left for user */ | |
800 | ||
801 | if (rcv_empty(tp)) | |
802 | { /* 14 */ | |
803 | t_close(tp, ECONNABORTED); | |
804 | return(CLOSED); | |
805 | } | |
806 | else /* 17 */ | |
807 | return(RCV_WAIT); | |
808 | ||
809 | } | |
810 | else if (tp->t_state == CLOSING1) /* 37 */ | |
811 | ||
812 | /* safe to close */ | |
813 | ||
814 | tp->waited_2_ml = TRUE; | |
815 | ||
816 | break; | |
817 | ||
818 | case TREXMT: /* retransmission timer */ | |
819 | ||
820 | if (is_unacked(tp)) | |
821 | { | |
822 | /* statistics */ | |
823 | tp->t_rxtct++; | |
824 | tcpstat.t_retransmit ++; | |
825 | ||
826 | /* | |
827 | * If we're retransmitting, then the network | |
828 | * may be dropping packets because it is overloaded. | |
829 | * Therefore, increase the retransmission time for | |
830 | * successive retransmissions. When we get an ACK, | |
831 | * the srtt and rxmitime will be recalculated. | |
832 | */ | |
833 | tp->t_rxmitime = tp->t_rxmitime << 1; | |
834 | if (tp->t_rxmitime > TCP_tvRXMAX) | |
835 | tp->t_rxmitime = TCP_tvRXMAX; | |
836 | ||
837 | tp->snd_nxt = tp->snd_una; | |
838 | tp->rexmt = TRUE; | |
839 | send_tcp(tp, TCP_DATA); | |
840 | tp->rexmt = FALSE; | |
841 | } | |
842 | break; | |
843 | ||
844 | case TREXMTTL: /* retransmit too long */ | |
845 | ||
846 | #ifdef HMPTRAPS | |
847 | /* hmp_trap(T_TCP_REXMTTL, (caddr_t)0, 0); */ | |
848 | #endif | |
849 | if (tp->usr_abort) | |
850 | { | |
851 | /* user has already closed for r/w so abort connection | |
852 | * usr_closed == closed for w (close or shutdown). | |
853 | */ | |
854 | t_close(tp, ETIMEDOUT); | |
855 | return(CLOSED); | |
856 | } | |
857 | advise_user(tcpcbtoso(tp), ETIMEDOUT); | |
858 | tp->t_timers[TREXMTTL] = tp->t_rttltimeo; | |
859 | break; | |
860 | ||
861 | case TPERSIST: /* persist timer */ | |
862 | ||
863 | /* force a byte send through closed window */ | |
864 | ||
865 | tp->force_one = TRUE; /* 38 */ | |
866 | send_tcp(tp, TCP_DATA); /* restarts timer */ | |
867 | tp->force_one = FALSE; | |
868 | break; | |
869 | ||
870 | case TDELACK: /* ack-delay timer */ | |
871 | ||
872 | /* make sure an ack gets sent now */ | |
873 | ||
874 | tp->force_ack = TRUE; | |
875 | send_tcp(tp, TCP_CTL); | |
876 | break; | |
877 | ||
878 | case TNOACT: /* no activity timer */ | |
879 | /* | |
880 | * This timer is used for 2 reasons: | |
881 | * 1) by the user to determine if the connection is idle or if the | |
882 | * other side has aborted/rebooted... This is open states entry. | |
883 | * See tcp_newtcpcb() | |
884 | * 2) by the system to timeout on receipt of ACK of our FIN. | |
885 | * This is separate from use of FINACK timer for other guy | |
886 | * to get our ACK of his FIN. If closing has started, finish it. | |
887 | */ | |
888 | ||
889 | /* | |
890 | * if its a shutdown(), | |
891 | * usr_closed == TRUE, usr_abort == FALSE | |
892 | * the user will find out about any problems getting an ACK of our | |
893 | * FIN through the retransmit took too long timer | |
894 | * the connection could be idle because it takes the remote end a | |
895 | * while to compute and produce a reply | |
896 | * user only gets to crank up protocol close once, but he can | |
897 | * shutdown and then close, thereby adjusting usr_abort so | |
898 | * that things get cleaned up if the remote host died. | |
899 | * | |
900 | * if its a close(), | |
901 | * usr_closed == TRUE, usr_abort == TRUE | |
902 | * user could be lingering (and SS_NOFDREF will still be false) | |
903 | * connection could be idle because the other host failed, and it | |
904 | * could be down for days. We don't want to wait for it to | |
905 | * come back up and give us a reset. Release resources now. | |
906 | */ | |
907 | if (tp->usr_abort) | |
908 | { | |
909 | t_close(tp, ETIMEDOUT); | |
910 | return(CLOSED); | |
911 | } | |
912 | ||
913 | if (tp->t_noactprobe) | |
914 | send_tcp(tp, TCP_CTL); | |
915 | ||
916 | if (tp->t_noactsig) | |
917 | advise_user(tcpcbtoso(tp), ETIMEDOUT); | |
918 | ||
919 | tp->t_timers[TNOACT] = tp->t_noact; | |
920 | break; | |
921 | ||
922 | } | |
923 | return(SAME); | |
924 | } |