1 /* $NetBSD: tp.trans,v 1.6.62.5 2005/03/04 16:54:08 skrll Exp $ */
5 * Copyright (c) 1991, 1993
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * @(#)tp.trans 8.1 (Berkeley) 6/10/93
35 /***********************************************************
36 Copyright IBM Corporation 1987
40 Permission to use, copy, modify, and distribute this software and its
41 documentation for any purpose and without fee is hereby granted,
42 provided that the above copyright notice appear in all copies and that
43 both that copyright notice and this permission notice appear in
44 supporting documentation, and that the name of IBM not be
45 used in advertising or publicity pertaining to distribution of the
46 software without specific, written prior permission.
48 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
49 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
50 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
51 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
52 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
53 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
56 ******************************************************************/
59 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
62 * Transition file for TP.
65 * - change the order of any of the events or states. to do so will
66 * make tppt, netstat, etc. cease working.
69 * some hooks exist for data on (dis)connect, but it's ***NOT***SUPPORTED***
70 * (read: may not work!)
72 * I tried to put everything that causes a change of state in here, hence
73 * there are some seemingly trivial events like T_DETACH and T_LISTEN_req.
75 * Almost everything having to do w/ setting & cancelling timers is here
76 * but once it was debugged, I moved the setting of the
77 * keepalive (sendack) timer to tp_emit(), where an AK_TPDU is sent.
78 * This is so the code wouldn't be duplicated all over creation in here.
85 /* @(#)tp.trans 8.1 (Berkeley) 6/10/93 */
86 #include <sys/param.h>
87 #include <sys/systm.h>
88 #include <sys/socket.h>
89 #include <sys/socketvar.h>
90 #include <sys/protosw.h>
93 #include <sys/errno.h>
95 #include <netiso/tp_param.h>
96 #include <netiso/tp_stat.h>
97 #include <netiso/tp_pcb.h>
98 #include <netiso/tp_tpdu.h>
99 #include <netiso/argo_debug.h>
100 #include <netiso/tp_trace.h>
101 #include <netiso/iso_errno.h>
102 #include <netiso/tp_seq.h>
103 #include <netiso/cons.h>
105 #define DRIVERTRACE TPPTdriver
106 #define sbwakeup(sb) sowakeup(p->tp_sock, sb);
107 #define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0)
112 tp_goodack(), tp_goodXack(),
115 void tp_indicate(), tp_getoptions(),
116 tp_soisdisconnecting(), tp_soisdisconnected(),
117 tp_recycle_tsuffix(),
118 #ifdef TP_DEBUG_TIMERS
119 tp_etimeout(), tp_euntimeout(),
120 tp_ctimeout(), tp_cuntimeout(),
123 tp_freeref(), tp_detach(),
124 tp0_stash(), tp0_send(),
125 tp_netcmd(), tp_send()
128 typedef struct tp_pcb tpcb_struct;
133 *PCB tpcb_struct SYNONYM P
143 TP_LISTENING /* Local to this implementation */
144 TP_CONFIRMING /* Local to this implementation */
146 *EVENTS { struct timeval e_time; } SYNONYM E
149 * C (typically cancelled) timers -
151 * let these be the first ones so for the sake of convenience
152 * their values are 0--> n-1
153 * DO NOT CHANGE THE ORDER OF THESE TIMER EVENTS!!
157 /* TM_retrans is used for all
158 * simple retransmissions - CR,CC,XPD,DR
162 /* TM_sendack does dual duty - keepalive AND closed-window
164 * It's set w/ keepalive-ticks every time an ack is sent.
165 * (this is done in (void) tp_emit() ).
166 * Whenever a DT arrives which doesn't require immediate acking,
167 * a separate fast-timeout flag is set ensuring 200ms response.
172 * E (typically expired) timers - these may be in any order.
173 * These cause procedures to be executed directly; may not
174 * cause an 'event' as we know them here.
176 TM_reference { SeqNum e_low; SeqNum e_high; int e_retrans; }
177 TM_data_retrans { SeqNum e_low; SeqNum e_high; int e_retrans; }
179 /* NOTE: in tp_input is a minor optimization that assumes that
180 * for all tpdu types that can take e_data and e_datalen, these
181 * fields fall in the same place in the event structure, that is,
182 * e_data is the first field and e_datalen is the 2nd field.
188 CR_TPDU { struct mbuf *e_data; /* first field */
189 int e_datalen; /* 2nd field */
192 DR_TPDU { struct mbuf *e_data; /* first field */
193 int e_datalen; /* 2nd field */
198 CC_TPDU { struct mbuf *e_data; /* first field */
199 int e_datalen; /* 2nd field */
203 AK_TPDU { u_int e_cdt;
206 u_char e_fcc_present;
208 DT_TPDU { struct mbuf *e_data; /* first field */
209 int e_datalen; /* 2nd field */
213 XPD_TPDU { struct mbuf *e_data; /* first field */
214 int e_datalen; /* 2nd field */
217 XAK_TPDU { SeqNum e_seq; }
220 T_DISC_req { u_char e_reason; }
234 /* TP_AKWAIT doesn't exist in TP 0 */
235 SAME <== TP_AKWAIT [ CC_TPDU, DC_TPDU, XAK_TPDU ]
241 /* applicable in TP4, TP0 */
242 SAME <== TP_REFWAIT DR_TPDU
245 (void) tp_emit(DC_TPDU_type, $P, 0, 0, NULL);
249 /* applicable in TP4, TP0 */
250 SAME <== TP_REFWAIT [ CR_TPDU, CC_TPDU, DT_TPDU,
251 DR_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU, DC_TPDU, ER_TPDU ]
255 if( $E.ev_number != AK_TPDU )
256 printf("TPDU 0x%x in REFWAIT!!!!\n", $E.ev_number);
261 /* applicable in TP4, TP0 */
262 SAME <== TP_REFWAIT [ T_DETACH, T_DISC_req ]
267 /* applicable in TP4, TP0 */
268 SAME <== TP_CRSENT AK_TPDU
269 ($P.tp_class == TP_CLASS_0)
271 /* oh, man is this grotesque or what? */
272 (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
273 /* but it's necessary because this pseudo-ack may happen
274 * before the CC arrives, but we HAVE to adjust the
275 * snduna as a result of the ack, WHENEVER it arrives
280 /* applicable in TP4, TP0 */
282 [ CR_TPDU, DC_TPDU, DT_TPDU, XPD_TPDU, XAK_TPDU ]
287 /* applicable in TP4, TP0 */
288 SAME <== TP_CLOSED [ DT_TPDU, XPD_TPDU,
289 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
294 /* TP_CLOSING doesn't exist in TP 0 */
296 [ CC_TPDU, CR_TPDU, DT_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU ]
302 /* DC_TPDU doesn't exist in TP 0 */
303 SAME <== TP_OPEN DC_TPDU
308 /* applicable in TP4, TP0 */
309 SAME <== TP_LISTENING [DR_TPDU, CC_TPDU, DT_TPDU, XPD_TPDU,
310 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ]
315 /* applicable in TP4, TP0 */
316 TP_LISTENING <== TP_CLOSED T_LISTEN_req
321 /* applicable in TP4, TP0 */
322 TP_CLOSED <== [ TP_LISTENING, TP_CLOSED ] T_DETACH
329 TP_CONFIRMING <== TP_LISTENING CR_TPDU
330 ( $P.tp_class == TP_CLASS_0)
332 $P.tp_refstate = REF_OPEN; /* has timers ??? */
336 TP_CONFIRMING <== TP_LISTENING CR_TPDU
340 tptrace(TPPTmisc, "CR datalen data", $$.e_datalen, $$.e_data,0,0);
343 printf("CR datalen 0x%x data 0x%x", $$.e_datalen, $$.e_data);
345 $P.tp_refstate = REF_OPEN; /* has timers */
346 $P.tp_fcredit = $$.e_cdt;
348 if ($$.e_datalen > 0) {
349 /* n/a for class 0 */
350 ASSERT($P.tp_Xrcv.sb_cc == 0);
351 sbappendrecord(&$P.tp_Xrcv, $$.e_data);
357 TP_OPEN <== TP_CONFIRMING T_ACPT_req
358 ( $P.tp_class == TP_CLASS_0 )
360 IncStat(ts_tp0_conn);
362 tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
365 printf("Confirming connection: $P" );
367 soisconnected($P.tp_sock);
368 (void) tp_emit(CC_TPDU_type, $P, 0,0, NULL) ;
373 TP_AKWAIT <== TP_CONFIRMING T_ACPT_req
374 (tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0)
376 IncStat(ts_tp4_conn); /* even though not quite open */
378 tptrace(TPPTmisc, "Confiming", $P, 0,0,0);
381 printf("Confirming connection: $P" );
384 soisconnecting($P.tp_sock);
385 if (($P.tp_rx_strat & TPRX_FASTSTART) && ($P.tp_fcredit > 0))
386 $P.tp_cong_win = $P.tp_fcredit * $P.tp_l_tpdusize;
387 $P.tp_retrans = $P.tp_Nretrans;
388 tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
393 TP_CLOSED <== TP_CONFIRMING T_ACPT_req
394 DEFAULT /* emit failed */
397 printf("event: CR_TPDU emit CC failed done " );
399 soisdisconnected($P.tp_sock);
400 tp_recycle_tsuffix($P);
401 tp_freeref($P.tp_lref);
406 /* applicable in TP4, TP0 */
407 TP_CRSENT <== TP_CLOSED T_CONN_req
411 struct mbuf *data = NULL;
414 tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags,
415 $P.tp_ucddata, 0, 0);
417 data = MCPY($P.tp_ucddata, M_WAIT);
420 printf("T_CONN_req.trans m_copy cc 0x%x\n",
422 dump_mbuf(data, "sosnd @ T_CONN_req");
426 if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) )
427 return error; /* driver WON'T change state; will return error */
429 $P.tp_refstate = REF_OPEN; /* has timers */
430 if($P.tp_class != TP_CLASS_0) {
431 $P.tp_retrans = $P.tp_Nretrans;
432 tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
437 /* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */
438 TP_REFWAIT <== [ TP_CRSENT, TP_AKWAIT, TP_OPEN ] DR_TPDU
441 sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */
442 if ($$.e_datalen > 0) {
443 sbappendrecord(&$P.tp_Xrcv, $$.e_data);
446 if ($P.tp_state == TP_OPEN)
447 tp_indicate(T_DISCONNECT, $P, 0);
449 int so_error = ECONNREFUSED;
450 if ($$.e_reason != (E_TP_NO_SESSION ^ TP_ERROR_MASK) &&
451 $$.e_reason != (E_TP_NO_CR_ON_NC ^ TP_ERROR_MASK) &&
452 $$.e_reason != (E_TP_REF_OVERFLOW ^ TP_ERROR_MASK))
453 so_error = ECONNABORTED;
454 tp_indicate(T_DISCONNECT, $P, so_error);
456 tp_soisdisconnected($P);
457 if ($P.tp_class != TP_CLASS_0) {
458 if ($P.tp_state == TP_OPEN ) {
459 tp_euntimeout($P, TM_data_retrans); /* all */
460 tp_cuntimeout($P, TM_retrans);
461 tp_cuntimeout($P, TM_inact);
462 tp_cuntimeout($P, TM_sendack);
463 $P.tp_flags &= ~TPF_DELACK;
465 tp_cuntimeout($P, TM_retrans);
467 (void) tp_emit(DC_TPDU_type, $P, 0, 0, NULL);
472 SAME <== TP_CLOSED DR_TPDU
476 (void) tp_emit(DC_TPDU_type, $P, 0, 0, NULL);
477 /* reference timer already set - reset it to be safe (???) */
478 tp_euntimeout($P, TM_reference); /* all */
479 tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
484 TP_REFWAIT <== TP_CRSENT ER_TPDU
487 tp_cuntimeout($P, TM_retrans);
488 tp_indicate(ER_TPDU, $P, $$.e_reason);
489 tp_soisdisconnected($P);
494 TP_REFWAIT <== TP_CLOSING DR_TPDU
497 tp_cuntimeout($P, TM_retrans);
498 tp_soisdisconnected($P);
501 /* these two transitions are the same but can't be combined because xebec
502 * can't handle the use of $$.e_reason if they're combined
505 TP_REFWAIT <== TP_CLOSING ER_TPDU
508 tp_indicate(ER_TPDU, $P, $$.e_reason);
509 tp_cuntimeout($P, TM_retrans);
510 tp_soisdisconnected($P);
514 TP_REFWAIT <== TP_CLOSING DC_TPDU
517 tp_cuntimeout($P, TM_retrans);
518 tp_soisdisconnected($P);
523 SAME <== TP_CLOSED [ CC_TPDU, CR_TPDU ]
525 { /* don't ask me why we have to do this - spec says so */
526 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, NULL);
527 /* don't bother with retransmissions of the DR */
532 TP_REFWAIT <== TP_OPEN ER_TPDU
533 ($P.tp_class == TP_CLASS_0)
535 tp_soisdisconnecting($P.tp_sock);
536 tp_indicate(ER_TPDU, $P, $$.e_reason);
537 tp_soisdisconnected($P);
538 tp_netcmd( $P, CONN_CLOSE );
542 TP_CLOSING <== [ TP_AKWAIT, TP_OPEN ] ER_TPDU
545 if ($P.tp_state == TP_OPEN) {
546 tp_euntimeout($P, TM_data_retrans); /* all */
547 tp_cuntimeout($P, TM_inact);
548 tp_cuntimeout($P, TM_sendack);
550 tp_soisdisconnecting($P.tp_sock);
551 tp_indicate(ER_TPDU, $P, $$.e_reason);
552 $P.tp_retrans = $P.tp_Nretrans;
553 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
554 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, NULL);
558 TP_OPEN <== TP_CRSENT CC_TPDU
559 ($P.tp_class == TP_CLASS_0)
561 tp_cuntimeout($P, TM_retrans);
562 IncStat(ts_tp0_conn);
564 soisconnected($P.tp_sock);
568 TP_OPEN <== TP_CRSENT CC_TPDU
572 printf("trans: CC_TPDU in CRSENT state flags 0x%x\n",
575 IncStat(ts_tp4_conn);
576 $P.tp_fref = $$.e_sref;
577 $P.tp_fcredit = $$.e_cdt;
578 if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0))
579 $P.tp_cong_win = $$.e_cdt * $P.tp_l_tpdusize;
581 tp_cuntimeout($P, TM_retrans);
584 printf("dropping user connect data cc 0x%x\n",
585 $P.tp_ucddata->m_len);
587 m_freem($P.tp_ucddata);
590 soisconnected($P.tp_sock);
591 if ($$.e_datalen > 0) {
592 ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */
593 sbappendrecord(&$P.tp_Xrcv, $$.e_data);
597 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, NULL);
598 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
603 SAME <== TP_CRSENT TM_retrans
604 ( $P.tp_retrans > 0 )
606 struct mbuf *data = NULL;
609 IncStat(ts_retrans_cr);
610 $P.tp_cong_win = 1 * $P.tp_l_tpdusize;
611 data = MCPY($P.tp_ucddata, M_NOWAIT);
614 printf("TM_retrans.trans m_copy cc 0x%x\n", data);
615 dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans");
622 if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) {
623 $P.tp_sock->so_error = error;
625 tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks);
630 TP_REFWAIT <== TP_CRSENT TM_retrans
631 DEFAULT /* no more CR retransmissions */
633 IncStat(ts_conn_gaveup);
634 $P.tp_sock->so_error = ETIMEDOUT;
635 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
636 tp_soisdisconnected($P);
641 SAME <== TP_AKWAIT CR_TPDU
643 /* duplicate CR (which doesn't really exist in the context of
644 * a connectionless network layer)
645 * Doesn't occur in class 0.
649 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
651 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) {
652 $P.tp_sock->so_error = error;
654 $P.tp_retrans = $P.tp_Nretrans;
655 tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
660 TP_OPEN <== TP_AKWAIT DT_TPDU
661 ( IN_RWINDOW( $P, $$.e_seq,
662 $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
667 * Get rid of any confirm or connect data, so that if we
668 * crash or close, it isn't thought of as disconnect data.
671 m_freem($P.tp_ucddata);
674 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
675 tp_cuntimeout($P, TM_retrans);
676 soisconnected($P.tp_sock);
677 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
679 /* see also next 2 transitions, if you make any changes */
681 doack = tp_stash($P, $E);
683 printf("tp_stash returns %d\n",doack);
687 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, NULL );
688 tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
690 tp_ctimeout( $P, TM_sendack, (int)$P.tp_sendack_ticks);
693 printf("after stash calling sbwakeup\n");
698 SAME <== TP_OPEN DT_TPDU
699 ( $P.tp_class == TP_CLASS_0 )
702 sbwakeup( &$P.tp_sock->so_rcv );
705 printf("after stash calling sbwakeup\n");
711 SAME <== TP_OPEN DT_TPDU
712 ( IN_RWINDOW( $P, $$.e_seq,
713 $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) )
715 int doack; /* tells if we must ack immediately */
717 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
718 sbwakeup( &$P.tp_sock->so_rcv );
720 doack = tp_stash($P, $E);
722 printf("tp_stash returns %d\n",doack);
726 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, NULL );
728 tp_ctimeout_MIN( $P, TM_sendack, (int)$P.tp_sendack_ticks);
731 printf("after stash calling sbwakeup\n");
736 /* Not in window - we must ack under certain circumstances, namely
737 * a) if the seq number is below lwe but > lwe - (max credit ever given)
738 * (to handle lost acks) Can use max-possible-credit for this ^^^.
740 * b) seq number is > uwe but < uwe + previously sent & withdrawn credit
742 * (see 12.2.3.8.1 of ISO spec, p. 73)
743 * We just always ack.
746 SAME <== [ TP_OPEN, TP_AKWAIT ] DT_TPDU
747 DEFAULT /* Not in window */
750 tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ",
751 $$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0);
755 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
756 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, NULL );
761 TP_OPEN <== TP_AKWAIT AK_TPDU
765 m_freem($P.tp_ucddata);
768 (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq);
769 tp_cuntimeout($P, TM_retrans);
771 soisconnected($P.tp_sock);
773 struct socket *so = $P.tp_sock;
775 "called sosiconn: so so_state rcv.sb_sel rcv.sb_flags",
776 so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags);
778 "called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head",
779 so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head);
782 tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
783 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
788 TP_OPEN <== [ TP_OPEN, TP_AKWAIT ] XPD_TPDU
789 ($P.tp_Xrcvnxt == $$.e_seq)
791 if( $P.tp_state == TP_AKWAIT ) {
793 m_freem($P.tp_ucddata);
796 tp_cuntimeout($P, TM_retrans);
797 soisconnected($P.tp_sock);
798 tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks);
799 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
802 tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n",
803 $P.tp_Xrcvnxt,$$.e_seq, $$.e_datalen, $$.e_data->m_len);
806 $P.tp_sock->so_state |= SS_RCVATMARK;
807 $$.e_data->m_flags |= M_EOR;
808 sbinsertoob(&$P.tp_Xrcv, $$.e_data);
810 dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv");
812 tp_indicate(T_XDATA, $P, 0);
813 sbwakeup( &$P.tp_Xrcv );
815 (void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, NULL);
816 SEQ_INC($P, $P.tp_Xrcvnxt);
821 SAME <== TP_OPEN T_USR_Xrcvd
824 if( $P.tp_Xrcv.sb_cc == 0 ) {
825 /* kludge for select(): */
826 /* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */
830 * Ack only after the user receives the XPD. This is better for
831 * users that use one XPD right after another.
832 * Acking right away (the NEW WAY, see the prev. transition) is
833 * better for occasional * XPD, when the receiving user doesn't
834 * want to read the XPD immediately (which is session's behavior).
836 int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, NULL);
837 SEQ_INC($P, $P.tp_Xrcvnxt);
842 /* NOTE: presently if the user doesn't read the connection data
843 * before and expedited data PDU comes in, the connection data will
844 * be dropped. This is a bug. To avoid it, we need somewhere else
845 * to put the connection data.
846 * On the other hand, we need not to have it sitting around forever.
847 * This is a problem with the idea of trying to accommodate
848 * data on connect w/ a passive-open user interface.
852 SAME <== [ TP_AKWAIT, TP_OPEN ] XPD_TPDU
853 DEFAULT /* not in window or cdt==0 */
856 tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n",
857 $P.tp_Xrcvnxt, $$.e_seq, $P.tp_Xrcv.sb_cc , 0);
859 if( $P.tp_Xrcvnxt != $$.e_seq )
861 if( $P.tp_Xrcv.sb_cc ) {
862 /* might as well kick 'em again */
863 tp_indicate(T_XDATA, $P, 0);
867 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
868 /* don't send an xack because the xak gives "last one received", not
869 * "next one i expect" (dumb)
874 /* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries
875 * to detach all its "children"
876 * Also (CRSENT) when user kills a job that's doing a connect()
878 TP_REFWAIT <== TP_CRSENT T_DETACH
879 ($P.tp_class == TP_CLASS_0)
881 struct socket *so = $P.tp_sock;
883 /* detach from parent socket so it can finish closing */
885 if (!soqremque(so, 0) && !soqremque(so, 1))
886 panic("tp: T_DETACH");
889 tp_soisdisconnecting($P.tp_sock);
890 tp_netcmd( $P, CONN_CLOSE);
891 tp_soisdisconnected($P);
896 TP_CLOSING <== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ] T_DETACH
899 struct socket *so = $P.tp_sock;
900 struct mbuf *data = NULL;
902 /* detach from parent socket so it can finish closing */
904 if (!soqremque(so, 0) && !soqremque(so, 1))
905 panic("tp: T_DETACH");
908 if ($P.tp_state != TP_CLOSING) {
909 tp_soisdisconnecting($P.tp_sock);
910 data = MCPY($P.tp_ucddata, M_NOWAIT);
911 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data);
912 $P.tp_retrans = $P.tp_Nretrans;
913 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
918 TP_REFWAIT <== [ TP_OPEN, TP_CRSENT ] T_DISC_req
919 ( $P.tp_class == TP_CLASS_0 )
921 tp_soisdisconnecting($P.tp_sock);
922 tp_netcmd( $P, CONN_CLOSE);
923 tp_soisdisconnected($P);
928 TP_CLOSING <== [ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ] T_DISC_req
931 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
933 if($P.tp_state == TP_OPEN) {
934 tp_euntimeout($P, TM_data_retrans); /* all */
935 tp_cuntimeout($P, TM_inact);
936 tp_cuntimeout($P, TM_sendack);
937 $P.tp_flags &= ~TPF_DELACK;
941 printf("T_DISC_req.trans tp_ucddata 0x%x\n",
943 dump_mbuf(data, "ucddata @ T_DISC_req");
946 tp_soisdisconnecting($P.tp_sock);
947 $P.tp_retrans = $P.tp_Nretrans;
948 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
951 return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data);
956 SAME <== TP_AKWAIT TM_retrans
957 ( $P.tp_retrans > 0 )
960 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT);
962 IncStat(ts_retrans_cc);
964 $P.tp_cong_win = 1 * $P.tp_l_tpdusize;
966 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) )
967 $P.tp_sock->so_error = error;
968 tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks);
973 TP_CLOSING <== TP_AKWAIT TM_retrans
974 DEFAULT /* out of time */
976 IncStat(ts_conn_gaveup);
977 tp_soisdisconnecting($P.tp_sock);
978 $P.tp_sock->so_error = ETIMEDOUT;
979 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
980 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, NULL);
981 $P.tp_retrans = $P.tp_Nretrans;
982 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
986 /* the retrans timers had better go off BEFORE the inactivity timer does,
987 * if transmissions are going on.
988 * (i.e., TM_inact should be greater than timer for all retrans plus ack
992 TP_CLOSING <== TP_OPEN [ TM_inact, TM_retrans, TM_data_retrans ]
995 tp_euntimeout($P, TM_data_retrans); /* all */
996 tp_cuntimeout($P, TM_inact);
997 tp_cuntimeout($P, TM_sendack);
999 IncStat(ts_conn_gaveup);
1000 tp_soisdisconnecting($P.tp_sock);
1001 $P.tp_sock->so_error = ETIMEDOUT;
1002 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT);
1003 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, NULL);
1004 $P.tp_retrans = $P.tp_Nretrans;
1005 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
1010 SAME <== TP_OPEN TM_retrans
1011 ( $P.tp_retrans > 0 )
1013 $P.tp_cong_win = 1 * $P.tp_l_tpdusize;
1015 if ( $P.tp_Xsnd.sb_mb ) {
1016 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
1020 tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna",
1021 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
1025 dump_mbuf(m, "XPD retrans emitting M");
1027 IncStat(ts_retrans_xpd);
1029 shift = max($P.tp_Nretrans - $P.tp_retrans, 6);
1030 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1031 tp_ctimeout($P, TM_retrans, ((int)$P.tp_dt_ticks) << shift);
1037 SAME <== TP_OPEN TM_data_retrans
1038 ($P.tp_rxtshift < TP_NRETRANS)
1041 (void) tp_data_retrans($P);
1046 SAME <== TP_CLOSING TM_retrans
1047 ( $P.tp_retrans > 0 )
1050 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, NULL);
1051 IncStat(ts_retrans_dr);
1052 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks);
1057 TP_REFWAIT <== TP_CLOSING TM_retrans
1058 DEFAULT /* no more retrans - gave up */
1060 $P.tp_sock->so_error = ETIMEDOUT;
1061 $P.tp_refstate = REF_FROZEN;
1062 tp_recycle_tsuffix( $P );
1063 tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks);
1068 * The resources are kept around until the ref timer goes off.
1069 * The suffixes are wiped out sooner so they can be reused right away.
1071 /* applicable in TP4, TP0 */
1072 TP_CLOSED <== TP_REFWAIT TM_reference
1075 tp_freeref($P.tp_lref);
1080 /* applicable in TP4, TP0 */
1081 /* A duplicate CR from connectionless network layer can't happen */
1082 SAME <== TP_OPEN [ CR_TPDU, CC_TPDU ]
1085 if( $P.tp_class != TP_CLASS_0) {
1086 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1087 if ( $E.ev_number == CC_TPDU )
1088 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, NULL);
1090 /* ignore it if class 0 - state tables are blank for this */
1094 /* applicable in TP4, TP0 */
1095 SAME <== TP_OPEN T_DATA_req
1099 tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb",
1100 $P.tp_sndnxt, $P.tp_snduna, $P.tp_fcredit, $P);
1108 SAME <== TP_OPEN T_XPD_req
1110 /* T_XPD_req was issued by sosend iff xpd socket buf was empty
1111 * at time of sosend(),
1112 * AND (which means) there were no unacknowledged XPD tpdus outstanding!
1118 if ( $P.tp_Xsnd.sb_mb ) {
1119 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc);
1120 /* m_copy doesn't preserve the m_xlink field, but at this pt.
1121 * that doesn't matter
1125 tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna",
1126 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt,
1130 printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc);
1131 dump_mbuf(m, "XPD req emitting M");
1134 tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1135 $P.tp_retrans = $P.tp_Nretrans;
1137 tp_ctimeout($P, TM_retrans, (int)$P.tp_rxtcur);
1138 SEQ_INC($P, $P.tp_Xsndnxt);
1145 /* TP4, faked ack in TP0 when cons send completes */
1146 SAME <== TP_OPEN AK_TPDU
1147 ( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq) )
1149 /* tp_goodack == true means
1150 * EITHER it actually acked something heretofore unacknowledged
1151 * OR no news but the credit should be processed.
1154 struct sockbuf *sb = &$P.tp_sock->so_snd;
1157 printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt);
1159 if( $P.tp_class != TP_CLASS_0) {
1160 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1164 printf("GOOD ACK new sndnxt 0x%x\n", $P.tp_sndnxt);
1169 /* TP4, and TP0 after sending a CC or possibly a CR */
1170 SAME <== TP_OPEN AK_TPDU
1174 tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq",
1175 $$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0);
1177 if( $P.tp_class != TP_CLASS_0 ) {
1179 if ( !$$.e_fcc_present ) {
1180 /* send ACK with FCC */
1181 IncStat( ts_ackreason[_ACK_FCC_] );
1182 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, NULL);
1184 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1190 /* goes in at *** */
1191 /* just so happens that this is never true now, because we allow
1192 * only 1 packet in the queue at once (this could be changed)
1193 if ( $P.tp_Xsnd.sb_mb ) {
1194 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??);
1196 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m);
1197 $P.tp_retrans = $P.tp_Nretrans;
1198 tp_ctimeout($P, TM_retrans, (int)$P.tp_xpd_ticks);
1199 SEQ_INC($P, $P.tp_Xsndnxt);
1202 /* end of the above hack */
1205 SAME <== TP_OPEN XAK_TPDU
1206 ( tp_goodXack($P, $$.e_seq) )
1207 /* tp_goodXack checks for good ack, removes the correct
1208 * tpdu from the queue and returns 1 if ack was legit, 0 if not.
1209 * also updates tp_Xuna
1212 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1213 tp_cuntimeout($P, TM_retrans);
1215 sbwakeup( &$P.tp_sock->so_snd );
1217 /* resume normal data */
1222 /* TP4, and TP0 after sending a CC or possibly a CR */
1223 SAME <== TP_OPEN XAK_TPDU
1227 tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0);
1229 if( $P.tp_class != TP_CLASS_0 ) {
1230 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks);
1236 SAME <== TP_OPEN TM_sendack
1241 tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe,
1242 $P.tp_sent_lcdt, 0);
1244 IncPStat($P, tps_n_TMsendack);
1245 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, NULL);
1246 if ($P.tp_fcredit == 0) {
1247 if ($P.tp_rxtshift < TP_MAXRXTSHIFT)
1249 timo = ($P.tp_dt_ticks) << $P.tp_rxtshift;
1251 timo = $P.tp_sendack_ticks;
1252 tp_ctimeout($P, TM_sendack, timo);
1257 SAME <== TP_OPEN T_USR_rcvd
1258 ($P.tp_class == TP_CLASS_0)
1260 if (sbspace(&$P.tp_sock->so_rcv) > 0)
1266 /* If old credit was zero,
1267 * we'd better inform other side that we now have space
1268 * But this is not enough. Sender might not yet have
1269 * seen an ack with cdt 0 but it might still think the
1270 * window is closed, so it's going to wait.
1271 * Best to send an ack each time.
1272 * Strictly speaking, this ought to be a function of the
1273 * general ack strategy.
1275 SAME <== TP_OPEN T_USR_rcvd
1281 * If the upper window edge has advanced a reasonable
1282 * amount beyond what was known, send an ACK.
1283 * A reasonable amount is 2 packets, unless the max window
1284 * is only 1 or 2 packets, in which case we
1285 * should send an ack for any advance in the upper window edge.
1288 ack_thresh = SEQ_SUB($P, $P.tp_lcredit + $P.tp_rcvnxt,
1289 ($P.tp_maxlcredit > 2 ? 2 : 1));
1290 if (SEQ_GT($P, ack_thresh, $P.tp_sent_uwe)) {
1291 IncStat(ts_ackreason[_ACK_USRRCV_]);
1292 $P.tp_flags &= ~TPF_DELACK;
1293 return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, NULL);
1299 /* applicable in TP4, TP0 */
1300 SAME <== TP_REFWAIT [ T_USR_rcvd, T_USR_Xrcvd ]
1302 /* This happens if other end sent a DR when the user was waiting
1304 * Processing the DR includes putting us in REFWAIT state.
1308 return ECONNABORTED;
1313 TP_REFWAIT <== [ TP_OPEN, TP_CRSENT, TP_LISTENING ] T_NETRESET
1314 ( $P.tp_class != TP_CLASS_4 )
1315 /* 0 or (4 and 0) */
1316 /* in OPEN class will be 0 or 4 but not both */
1317 /* in CRSENT or LISTENING it could be in negotiation, hence both */
1318 /* Actually, this shouldn't ever happen in LISTENING */
1320 ASSERT( $P.tp_state != TP_LISTENING );
1321 tp_indicate(T_DISCONNECT, $P, ECONNRESET);
1322 tp_soisdisconnected($P);
1326 /* TP4: ignore resets */
1327 SAME <== [ TP_OPEN, TP_CRSENT, TP_AKWAIT,
1328 TP_CLOSING, TP_LISTENING ] T_NETRESET
1333 /* applicable in TP4, TP0 */
1334 SAME <== [ TP_CLOSED, TP_REFWAIT ] T_NETRESET