1/*********************************************************** 2 Copyright IBM Corporation 1987 3 4 All Rights Reserved 5 6Permission to use, copy, modify, and distribute this software and its 7documentation for any purpose and without fee is hereby granted, 8provided that the above copyright notice appear in all copies and that 9both that copyright notice and this permission notice appear in 10supporting documentation, and that the name of IBM not be 11used in advertising or publicity pertaining to distribution of the 12software without specific, written prior permission. 13 14IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20SOFTWARE. 21 22******************************************************************/ 23 24/* 25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison 26 */ 27/* $Header: tp.trans,v 5.1 88/10/12 12:22:07 root Exp $ 28 * 29 * Transition file for TP. 30 * 31 * DO NOT: 32 * - change the order of any of the events or states. to do so will 33 * make tppt, netstat, etc. cease working. 34 * 35 * NOTE: 36 * some hooks exist for data on (dis)connect, but it's ***NOT***SUPPORTED*** 37 * (read: may not work!) 38 * 39 * I tried to put everything that causes a change of state in here, hence 40 * there are some seemingly trivial events like T_DETACH and T_LISTEN_req. 41 * 42 * Almost everything having to do w/ setting & cancelling timers is here 43 * but once it was debugged, I moved the setting of the 44 * keepalive (sendack) timer to tp_emit(), where an AK_TPDU is sent. 45 * This is so the code wouldn't be duplicated all over creation in here. 46 * 47 */ 48*PROTOCOL tp 49 50*INCLUDE 51 52{ 53/* @(#)tp.trans 7.3 (Berkeley) 08/29/89 */ 54#include "param.h" 55#include "socket.h" 56#include "socketvar.h" 57#include "protosw.h" 58#include "mbuf.h" 59#include "time.h" 60#include "errno.h" 61#include "../netiso/tp_param.h" 62#include "../netiso/tp_stat.h" 63#include "../netiso/tp_pcb.h" 64#include "../netiso/tp_tpdu.h" 65#include "../netiso/argo_debug.h" 66#include "../netiso/tp_trace.h" 67#include "../netiso/iso_errno.h" 68#include "../netiso/tp_seq.h" 69#include "../netiso/cons.h" 70 71#define DRIVERTRACE TPPTdriver 72#define sbwakeup(sb) sowakeup(p->tp_sock, sb); 73#define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0) 74 75static trick_hc = 1; 76 77int tp_emit(), 78 tp_goodack(), tp_goodXack(), 79 tp_stash() 80; 81void tp_indicate(), tp_getoptions(), 82 tp_soisdisconnecting(), tp_soisdisconnected(), 83 tp_recycle_tsuffix(), 84 tp_etimeout(), tp_euntimeout(), 85 tp_euntimeout_lss(), tp_ctimeout(), 86 tp_cuntimeout(), tp_ctimeout_MIN(), 87 tp_freeref(), tp_detach(), 88 tp0_stash(), tp0_send(), 89 tp_netcmd(), tp_send() 90; 91 92typedef struct tp_pcb tpcb_struct; 93 94 95} 96 97*PCB tpcb_struct SYNONYM P 98 99*STATES 100 101TP_CLOSED 102TP_CRSENT 103TP_AKWAIT 104TP_OPEN 105TP_CLOSING 106TP_REFWAIT 107TP_LISTENING /* Local to this implementation */ 108TP_CONFIRMING /* Local to this implementation */ 109 110*EVENTS { struct timeval e_time; } SYNONYM E 111 112 /* 113 * C (typically cancelled) timers - 114 * 115 * let these be the first ones so for the sake of convenience 116 * their values are 0--> n-1 117 * DO NOT CHANGE THE ORDER OF THESE TIMER EVENTS!! 118 */ 119 TM_inact 120 TM_retrans 121 /* TM_retrans is used for all 122 * simple retransmissions - CR,CC,XPD,DR 123 */ 124 125 TM_sendack 126 /* TM_sendack does dual duty - keepalive AND sendack. 127 * It's set w/ keepalive-ticks every time an ack is sent. 128 * (this is done in (void) tp_emit() ). 129 * It's cancelled and reset whenever a DT 130 * arrives and it doesn't require immediate acking. 131 * Note that in this case it's set w/ the minimum of 132 * its prev value and the sendack-ticks value so the 133 * purpose of the keepalive is preserved. 134 */ 135 TM_notused 136 137 /* 138 * E (typically expired) timers - these may be in any order. 139 * These cause procedures to be executed directly; may not 140 * cause an 'event' as we know them here. 141 */ 142 TM_reference { SeqNum e_low; SeqNum e_high; int e_retrans; } 143 TM_data_retrans { SeqNum e_low; SeqNum e_high; int e_retrans; } 144 145/* NOTE: in tp_input is a minor optimization that assumes that 146 * for all tpdu types that can take e_data and e_datalen, these 147 * fields fall in the same place in the event structure, that is, 148 * e_data is the first field and e_datalen is the 2nd field. 149 */ 150 151 ER_TPDU { 152 u_char e_reason; 153 } 154 CR_TPDU { struct mbuf *e_data; /* first field */ 155 int e_datalen; /* 2nd field */ 156 u_int e_cdt; 157 } 158 DR_TPDU { struct mbuf *e_data; /* first field */ 159 int e_datalen; /* 2nd field */ 160 u_short e_sref; 161 u_char e_reason; 162 } 163 DC_TPDU 164 CC_TPDU { struct mbuf *e_data; /* first field */ 165 int e_datalen; /* 2nd field */ 166 u_short e_sref; 167 u_int e_cdt; 168 } 169 AK_TPDU { u_int e_cdt; 170 SeqNum e_seq; 171 SeqNum e_subseq; 172 u_char e_fcc_present; 173 } 174 DT_TPDU { struct mbuf *e_data; /* first field */ 175 int e_datalen; /* 2nd field */ 176 u_int e_eot; 177 SeqNum e_seq; 178 } 179 XPD_TPDU { struct mbuf *e_data; /* first field */ 180 int e_datalen; /* 2nd field */ 181 SeqNum e_seq; 182 } 183 XAK_TPDU { SeqNum e_seq; } 184 185 T_CONN_req 186 T_DISC_req { u_char e_reason; } 187 T_LISTEN_req 188 T_DATA_req 189 T_XPD_req 190 T_USR_rcvd 191 T_USR_Xrcvd 192 T_DETACH 193 T_NETRESET 194 T_ACPT_req 195 196 197*TRANSITIONS 198 199 200/* TP_AKWAIT doesn't exist in TP 0 */ 201SAME <== TP_AKWAIT [ CC_TPDU, DC_TPDU, XAK_TPDU ] 202 DEFAULT 203 NULLACTION 204; 205 206 207/* applicable in TP4, TP0 */ 208SAME <== TP_REFWAIT DR_TPDU 209 ( $$.e_sref != 0 ) 210 { 211 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); 212 } 213; 214 215/* applicable in TP4, TP0 */ 216SAME <== TP_REFWAIT [ CR_TPDU, CC_TPDU, DT_TPDU, 217 DR_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU, DC_TPDU, ER_TPDU ] 218 DEFAULT 219 { 220# ifdef TP_DEBUG 221 if( $E.ev_number != AK_TPDU ) 222 printf("TPDU 0x%x in REFWAIT!!!!\n", $E.ev_number); 223# endif TP_DEBUG 224 } 225; 226 227/* applicable in TP4, TP0 */ 228SAME <== TP_REFWAIT [ T_DETACH, T_DISC_req ] 229 DEFAULT 230 NULLACTION 231; 232 233/* applicable in TP4, TP0 */ 234SAME <== TP_CRSENT AK_TPDU 235 ($P.tp_class == TP_CLASS_0) 236 { 237 /* oh, man is this grotesque or what? */ 238 (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq); 239 /* but it's necessary because this pseudo-ack may happen 240 * before the CC arrives, but we HAVE to adjust the 241 * snduna as a result of the ack, WHENEVER it arrives 242 */ 243 } 244; 245 246/* applicable in TP4, TP0 */ 247SAME <== TP_CRSENT 248 [ CR_TPDU, DC_TPDU, DT_TPDU, XPD_TPDU, XAK_TPDU ] 249 DEFAULT 250 NULLACTION 251; 252 253/* applicable in TP4, TP0 */ 254SAME <== TP_CLOSED [ DT_TPDU, XPD_TPDU, 255 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ] 256 DEFAULT 257 NULLACTION 258; 259 260/* TP_CLOSING doesn't exist in TP 0 */ 261SAME <== TP_CLOSING 262 [ CC_TPDU, CR_TPDU, DT_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU ] 263 DEFAULT 264 NULLACTION 265; 266 267 268/* DC_TPDU doesn't exist in TP 0 */ 269SAME <== TP_OPEN DC_TPDU 270 DEFAULT 271 NULLACTION 272; 273 274/* applicable in TP4, TP0 */ 275SAME <== TP_LISTENING [DR_TPDU, CC_TPDU, DT_TPDU, XPD_TPDU, 276 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ] 277 DEFAULT 278 NULLACTION 279; 280 281/* applicable in TP4, TP0 */ 282TP_LISTENING <== TP_CLOSED T_LISTEN_req 283 DEFAULT 284 NULLACTION 285; 286 287/* applicable in TP4, TP0 */ 288TP_CLOSED <== [ TP_LISTENING, TP_CLOSED ] T_DETACH 289 DEFAULT 290 { 291 tp_detach($P); 292 } 293; 294 295TP_CONFIRMING <== TP_LISTENING CR_TPDU 296 ( $P.tp_class == TP_CLASS_0) 297 { 298 $P.tp_refp->tpr_state = REF_OPEN; /* has timers ??? */ 299 } 300; 301 302TP_CONFIRMING <== TP_LISTENING CR_TPDU 303 DEFAULT 304 { 305 IFTRACE(D_CONN) 306 tptrace(TPPTmisc, "CR datalen data", $$.e_datalen, $$.e_data,0,0); 307 ENDTRACE 308 IFDEBUG(D_CONN) 309 printf("CR datalen 0x%x data 0x%x", $$.e_datalen, $$.e_data); 310 ENDDEBUG 311 $P.tp_refp->tpr_state = REF_OPEN; /* has timers */ 312 $P.tp_fcredit = $$.e_cdt; 313 314 if ($$.e_datalen > 0) { 315 /* n/a for class 0 */ 316 ASSERT($P.tp_Xrcv.sb_cc == 0); 317 sbappendrecord(&$P.tp_Xrcv, $$.e_data); 318 /*$P.tp_flags |= TPF_CONN_DATA_IN;*/ 319 $$.e_data = MNULL; 320 } 321 } 322; 323 324TP_OPEN <== TP_CONFIRMING T_ACPT_req 325 ( $P.tp_class == TP_CLASS_0 ) 326 { 327 IncStat(ts_tp0_conn); 328 IFTRACE(D_CONN) 329 tptrace(TPPTmisc, "Confiming", $P, 0,0,0); 330 ENDTRACE 331 IFDEBUG(D_CONN) 332 printf("Confirming connection: $P" ); 333 ENDDEBUG 334 soisconnected($P.tp_sock); 335 (void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ; 336 $P.tp_fcredit = 1; 337 } 338; 339 340TP_AKWAIT <== TP_CONFIRMING T_ACPT_req 341 (tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0) 342 { 343 IncStat(ts_tp4_conn); /* even though not quite open */ 344 IFTRACE(D_CONN) 345 tptrace(TPPTmisc, "Confiming", $P, 0,0,0); 346 ENDTRACE 347 IFDEBUG(D_CONN) 348 printf("Confirming connection: $P" ); 349 ENDDEBUG 350 soisconnecting($P.tp_sock); 351 if($P.tp_rx_strat & TPRX_FASTSTART) 352 $P.tp_cong_win = $P.tp_fcredit; 353 $P.tp_retrans = $P.tp_Nretrans; 354 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks); 355 } 356; 357 358/* TP4 only */ 359TP_CLOSED <== TP_CONFIRMING T_ACPT_req 360 DEFAULT /* emit failed */ 361 { 362 register struct tp_ref *r = $P.tp_refp; 363 364 IFDEBUG(D_CONN) 365 printf("event: CR_TPDU emit CC failed done " ); 366 ENDDEBUG 367 soisdisconnected($P.tp_sock); 368 tp_recycle_tsuffix( $P ); 369 tp_freeref(r); 370 tp_detach($P); 371 } 372; 373 374/* applicable in TP4, TP0 */ 375TP_CRSENT <== TP_CLOSED T_CONN_req 376 DEFAULT 377 { 378 int error; 379 struct mbuf *data = MNULL; 380 381 IFTRACE(D_CONN) 382 tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags, 383 $P.tp_ucddata, 0, 0); 384 ENDTRACE 385 data = MCPY($P.tp_ucddata, M_WAIT); 386 if (data) { 387 IFDEBUG(D_CONN) 388 printf("T_CONN_req.trans m_copy cc 0x%x\n", 389 $P.tp_ucddata); 390 dump_mbuf(data, "sosnd @ T_CONN_req"); 391 ENDDEBUG 392 } 393 394 if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) 395 return error; /* driver WON'T change state; will return error */ 396 397 $P.tp_refp->tpr_state = REF_OPEN; /* has timers */ 398 if($P.tp_class != TP_CLASS_0) { 399 $P.tp_retrans = $P.tp_Nretrans; 400 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks); 401 } 402 } 403; 404 405/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */ 406TP_REFWAIT <== [ TP_CRSENT, TP_AKWAIT, TP_OPEN ] DR_TPDU 407 DEFAULT 408 { 409 if ($$.e_datalen > 0 && $P.tp_class != TP_CLASS_0) { 410 /*sbdrop(&$P.tp_Xrcv, $P.tp_Xrcv.sb_cc); /* purge expedited data */ 411 sbflush(&$P.tp_Xrcv); 412 $P.tp_flags |= TPF_DISC_DATA_IN; 413 sbappendrecord(&$P.tp_Xrcv, $$.e_data); 414 $$.e_data = MNULL; 415 } 416 tp_indicate(T_DISCONNECT, $P, TP_ERROR_MASK | (u_short)$$.e_reason); 417 tp_soisdisconnected($P); 418 if ($P.tp_class != TP_CLASS_0) { 419 if ($P.tp_state == TP_OPEN ) { 420 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */ 421 tp_cuntimeout($P.tp_refp, TM_retrans); 422 tp_cuntimeout($P.tp_refp, TM_inact); 423 tp_cuntimeout($P.tp_refp, TM_sendack); 424 } 425 tp_cuntimeout($P.tp_refp, TM_retrans); 426 if( $$.e_sref != 0 ) 427 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); 428 } 429 } 430; 431 432SAME <== TP_CLOSED DR_TPDU 433 DEFAULT 434 { 435 if( $$.e_sref != 0 ) 436 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); 437 /* reference timer already set - reset it to be safe (???) */ 438 tp_euntimeout($P.tp_refp, TM_reference); /* all */ 439 tp_etimeout($P.tp_refp, TM_reference, 0, 0, 0, (int)$P.tp_refer_ticks); 440 } 441; 442 443/* NBS(34) */ 444TP_REFWAIT <== TP_CRSENT ER_TPDU 445 DEFAULT 446 { 447 tp_cuntimeout($P.tp_refp, TM_retrans); 448 tp_indicate(T_DISCONNECT, $P, 449 TP_ERROR_MASK |(u_short)($$.e_reason | 0x40)); 450 tp_soisdisconnected($P); 451 } 452; 453 454/* NBS(27) */ 455TP_REFWAIT <== TP_CLOSING DR_TPDU 456 DEFAULT 457 { 458 $P.tp_sock->so_error = (u_short)$$.e_reason; 459 tp_cuntimeout($P.tp_refp, TM_retrans); 460 tp_soisdisconnected($P); 461 } 462; 463/* these two transitions are the same but can't be combined because xebec 464 * can't handle the use of $$.e_reason if they're combined 465 */ 466/* NBS(27) */ 467TP_REFWAIT <== TP_CLOSING ER_TPDU 468 DEFAULT 469 { 470 $P.tp_sock->so_error = (u_short)$$.e_reason; 471 tp_cuntimeout($P.tp_refp, TM_retrans); 472 tp_soisdisconnected($P); 473 } 474; 475/* NBS(27) */ 476TP_REFWAIT <== TP_CLOSING DC_TPDU 477 DEFAULT 478 { 479 tp_cuntimeout($P.tp_refp, TM_retrans); 480 tp_soisdisconnected($P); 481 } 482; 483 484/* NBS(21) */ 485SAME <== TP_CLOSED [ CC_TPDU, CR_TPDU ] 486 DEFAULT 487 { /* don't ask me why we have to do this - spec says so */ 488 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL); 489 /* don't bother with retransmissions of the DR */ 490 } 491; 492 493/* NBS(34) */ 494TP_REFWAIT <== TP_OPEN ER_TPDU 495 ($P.tp_class == TP_CLASS_0) 496 { 497 tp_soisdisconnecting($P.tp_sock); 498 tp_indicate(T_DISCONNECT, $P, 499 TP_ERROR_MASK |(u_short)($$.e_reason | 0x40)); 500 501 tp_soisdisconnected($P); 502 tp_netcmd( $P, CONN_CLOSE ); 503 } 504; 505 506TP_CLOSING <== [ TP_AKWAIT, TP_OPEN ] ER_TPDU 507 DEFAULT 508 { 509 if ($P.tp_state == TP_OPEN) { 510 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */ 511 tp_cuntimeout($P.tp_refp, TM_inact); 512 tp_cuntimeout($P.tp_refp, TM_sendack); 513 } 514 tp_soisdisconnecting($P.tp_sock); 515 tp_indicate(T_DISCONNECT, $P, 516 TP_ERROR_MASK |(u_short)($$.e_reason | 0x40)); 517 $P.tp_retrans = $P.tp_Nretrans; 518 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 519 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL); 520 } 521; 522/* NBS(6) */ 523TP_OPEN <== TP_CRSENT CC_TPDU 524 ($P.tp_class == TP_CLASS_0) 525 { 526 tp_cuntimeout($P.tp_refp, TM_retrans); 527 IncStat(ts_tp0_conn); 528 $P.tp_fcredit = 1; 529 soisconnected($P.tp_sock); 530 } 531; 532 533TP_OPEN <== TP_CRSENT CC_TPDU 534 DEFAULT 535 { 536 IFDEBUG(D_CONN) 537 printf("trans: CC_TPDU in CRSENT state flags 0x%x\n", 538 (int)$P.tp_flags); 539 ENDDEBUG 540 IncStat(ts_tp4_conn); 541 $P.tp_fref = $$.e_sref; 542 $P.tp_fcredit = $$.e_cdt; 543 if($P.tp_rx_strat & TPRX_FASTSTART) 544 $P.tp_cong_win = $$.e_cdt; 545 tp_getoptions($P); 546 tp_cuntimeout($P.tp_refp, TM_retrans); 547 if ($P.tp_ucddata) { 548 IFDEBUG(D_CONN) 549 printf("dropping user connect data cc 0x%x\n", 550 $P.tp_ucddata->m_len); 551 ENDDEBUG 552 m_freem($P.tp_ucddata); 553 $P.tp_ucddata = 0; 554 } 555 soisconnected($P.tp_sock); 556 if ($$.e_datalen > 0) { 557 ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */ 558 sbappendrecord(&$P.tp_Xrcv, $$.e_data); 559 $P.tp_flags |= TPF_CONN_DATA_IN; 560 $$.e_data = MNULL; 561 } 562 563 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 564 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 565 } 566; 567 568/* TP4 only */ 569SAME <== TP_CRSENT TM_retrans 570 ( $P.tp_retrans > 0 ) 571 { 572 struct mbuf *data = MNULL; 573 int error; 574 575 IncStat(ts_retrans_cr); 576 data = MCPY($P.tp_ucddata, M_NOWAIT); 577 if($P.tp_ucddata) { 578 IFDEBUG(D_CONN) 579 printf("TM_retrans.trans m_copy cc 0x%x\n", data); 580 dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans"); 581 ENDDEBUG 582 if( data == MNULL ) 583 return ENOBUFS; 584 } 585 586 $P.tp_retrans --; 587 if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) { 588 $P.tp_sock->so_error = error; 589 } 590 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks); 591 } 592; 593 594/* TP4 only */ 595TP_REFWAIT <== TP_CRSENT TM_retrans 596 DEFAULT /* no more CR retransmissions */ 597 { 598 IncStat(ts_conn_gaveup); 599 $P.tp_sock->so_error = ETIMEDOUT; 600 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 601 tp_soisdisconnected($P); 602 } 603; 604 605/* TP4 only */ 606SAME <== TP_AKWAIT CR_TPDU 607 DEFAULT 608 /* duplicate CR (which doesn't really exist in the context of 609 * a connectionless network layer) 610 * Doesn't occur in class 0. 611 */ 612 { 613 int error; 614 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 615 616 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) { 617 $P.tp_sock->so_error = error; 618 } 619 $P.tp_retrans = $P.tp_Nretrans; 620 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks); 621 } 622; 623 624/* TP4 only */ 625TP_OPEN <== TP_AKWAIT DT_TPDU 626 ( IN_RWINDOW( $P, $$.e_seq, 627 $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) ) 628 { 629 int doack; 630 631 /* 632 * Get rid of any confirm or connect data, so that if we 633 * crash or close, it isn't thought of as disconnect data. 634 */ 635 if ($P.tp_ucddata) { 636 m_freem($P.tp_ucddata); 637 $P.tp_ucddata = 0; 638 } 639 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 640 tp_cuntimeout($P.tp_refp, TM_retrans); 641 soisconnected($P.tp_sock); 642 tp_getoptions($P); 643 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 644 645 /* see also next 2 transitions, if you make any changes */ 646 647 doack = tp_stash($P, $E); 648 IFDEBUG(D_DATA) 649 printf("tp_stash returns %d\n",doack); 650 ENDDEBUG 651 652 if(doack) { 653 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); 654 tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks); 655 } else 656 tp_ctimeout( $P.tp_refp, TM_sendack, (int)$P.tp_sendack_ticks); 657 658 IFDEBUG(D_DATA) 659 printf("after stash calling sbwakeup\n"); 660 ENDDEBUG 661 } 662; 663 664SAME <== TP_OPEN DT_TPDU 665 ( $P.tp_class == TP_CLASS_0 ) 666 { 667 tp0_stash($P, $E); 668 sbwakeup( &$P.tp_sock->so_rcv ); 669 670 IFDEBUG(D_DATA) 671 printf("after stash calling sbwakeup\n"); 672 ENDDEBUG 673 } 674; 675 676/* TP4 only */ 677SAME <== TP_OPEN DT_TPDU 678 ( IN_RWINDOW( $P, $$.e_seq, 679 $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) ) 680 { 681 int doack; /* tells if we must ack immediately */ 682 683 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 684 sbwakeup( &$P.tp_sock->so_rcv ); 685 686 doack = tp_stash($P, $E); 687 IFDEBUG(D_DATA) 688 printf("tp_stash returns %d\n",doack); 689 ENDDEBUG 690 691 if(doack) 692 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); 693 else 694 tp_ctimeout_MIN( $P.tp_refp, TM_sendack, (int)$P.tp_sendack_ticks); 695 696 IFDEBUG(D_DATA) 697 printf("after stash calling sbwakeup\n"); 698 ENDDEBUG 699 } 700; 701 702/* Not in window - we must ack under certain circumstances, namely 703 * a) if the seq number is below lwe but > lwe - (max credit ever given) 704 * (to handle lost acks) Can use max-possible-credit for this ^^^. 705 * and 706 * b) seq number is > uwe but < uwe + previously sent & withdrawn credit 707 * 708 * (see 12.2.3.8.1 of ISO spec, p. 73) 709 * We just always ack. 710 */ 711/* TP4 only */ 712SAME <== [ TP_OPEN, TP_AKWAIT ] DT_TPDU 713 DEFAULT /* Not in window */ 714 { 715 IFTRACE(D_DATA) 716 tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ", 717 $$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0); 718 ENDTRACE 719 IncStat(ts_dt_niw); 720 m_freem($$.e_data); 721 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 722 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); 723 } 724; 725 726/* TP4 only */ 727TP_OPEN <== TP_AKWAIT AK_TPDU 728 DEFAULT 729 { 730 if ($P.tp_ucddata) { 731 m_freem($P.tp_ucddata); 732 $P.tp_ucddata = 0; 733 } 734 (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq); 735 tp_cuntimeout($P.tp_refp, TM_retrans); 736 737 tp_getoptions($P); 738 soisconnected($P.tp_sock); 739 IFTRACE(D_CONN) 740 struct socket *so = $P.tp_sock; 741 tptrace(TPPTmisc, 742 "called sosiconn: so so_state rcv.sb_sel rcv.sb_flags", 743 so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags); 744 tptrace(TPPTmisc, 745 "called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head", 746 so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head); 747 ENDTRACE 748 749 tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks); 750 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 751 } 752; 753 754/* TP4 only */ 755TP_OPEN <== [ TP_OPEN, TP_AKWAIT ] XPD_TPDU 756 ( $P.tp_Xrcvnxt == $$.e_seq /* && $P.tp_Xrcv.sb_cc == 0*/) 757 { 758 if( $P.tp_state == TP_AKWAIT ) { 759 if ($P.tp_ucddata) { 760 m_freem($P.tp_ucddata); 761 $P.tp_ucddata = 0; 762 } 763 tp_cuntimeout($P.tp_refp, TM_retrans); 764 tp_getoptions($P); 765 soisconnected($P.tp_sock); 766 tp_ctimeout($P.tp_refp, TM_sendack, (int)$P.tp_keepalive_ticks); 767 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 768 } 769 IFTRACE(D_XPD) 770 tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n", 771 $P.tp_Xrcvnxt,$$.e_seq, $$.e_datalen, $$.e_data->m_len); 772 ENDTRACE 773 774 $P.tp_sock->so_state |= SS_RCVATMARK; 775 sbinsertoob(&$P.tp_Xrcv, $$.e_data); 776 IFDEBUG(D_XPD) 777 dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv"); 778 ENDDEBUG 779 tp_indicate(T_XDATA, $P, 0); 780 sbwakeup( &$P.tp_Xrcv ); 781 782 (void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL); 783 SEQ_INC($P, $P.tp_Xrcvnxt); 784 } 785; 786 787/* TP4 only */ 788SAME <== TP_OPEN T_USR_Xrcvd 789 DEFAULT 790 { 791 if( $P.tp_Xrcv.sb_cc == 0 ) { 792 /*$P.tp_flags &= ~TPF_XPD_PRESENT;*/ 793 /* kludge for select(): */ 794 /* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */ 795 } 796 } 797 /* OLD WAY: 798 * Ack only after the user receives the XPD. This is better for 799 * users that use one XPD right after another. 800 * Acking right away (the NEW WAY, see the prev. transition) is 801 * better for occasional * XPD, when the receiving user doesn't 802 * want to read the XPD immediately (which is session's behavior). 803 * 804 int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL); 805 SEQ_INC($P, $P.tp_Xrcvnxt); 806 return error; 807 */ 808; 809 810/* NOTE: presently if the user doesn't read the connection data 811 * before and expedited data PDU comes in, the connection data will 812 * be dropped. This is a bug. To avoid it, we need somewhere else 813 * to put the connection data. 814 * On the other hand, we need not to have it sitting around forever. 815 * This is a problem with the idea of trying to accommodate 816 * data on connect w/ a passive-open user interface. 817 */ 818/* TP4 only */ 819 820SAME <== [ TP_AKWAIT, TP_OPEN ] XPD_TPDU 821 DEFAULT /* not in window or cdt==0 */ 822 { 823 IFTRACE(D_XPD) 824 tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n", 825 $P.tp_Xrcvnxt, $$.e_seq, $P.tp_Xrcv.sb_cc , 0); 826 ENDTRACE 827 if( $P.tp_Xrcvnxt != $$.e_seq ) 828 IncStat(ts_xpd_niw); 829 if( $P.tp_Xrcv.sb_cc ) { 830#ifdef notdef 831 if( $P.tp_flags & TPF_CONN_DATA_IN ) { 832 /* user isn't reading the connection data; see note above */ 833 sbdrop(&$P.tp_Xrcv, $P.tp_Xrcv.sb_cc); 834 $P.tp_flags &= ~TPF_CONN_DATA_IN; 835 } 836#endif notdef 837 /* might as well kick 'em again */ 838 tp_indicate(T_XDATA, $P, 0); 839 IncStat(ts_xpd_dup); 840 } 841 m_freem($$.e_data); 842 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 843 /* don't send an xack because the xak gives "last one received", not 844 * "next one i expect" (dumb) 845 */ 846 } 847; 848 849/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries 850 * to detach all its "children" 851 * Also (CRSENT) when user kills a job that's doing a connect() 852 */ 853TP_REFWAIT <== TP_CRSENT T_DETACH 854 ($P.tp_class == TP_CLASS_0) 855 { 856 struct socket *so = $P.tp_sock; 857 858 /* detach from parent socket so it can finish closing */ 859 if (so->so_head) { 860 if (!soqremque(so, 0) && !soqremque(so, 1)) 861 panic("tp: T_DETACH"); 862 so->so_head = 0; 863 } 864 tp_soisdisconnecting($P.tp_sock); 865 tp_netcmd( $P, CONN_CLOSE); 866 tp_soisdisconnected($P); 867 } 868; 869 870/* TP4 only */ 871TP_CLOSING <== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ] T_DETACH 872 DEFAULT 873 { 874 struct socket *so = $P.tp_sock; 875 struct mbuf *data = MNULL; 876 877 /* detach from parent socket so it can finish closing */ 878 if (so->so_head) { 879 if (!soqremque(so, 0) && !soqremque(so, 1)) 880 panic("tp: T_DETACH"); 881 so->so_head = 0; 882 } 883 if ($P.tp_state != TP_CLOSING) { 884 tp_soisdisconnecting($P.tp_sock); 885 data = MCPY($P.tp_ucddata, M_NOWAIT); 886 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data); 887 $P.tp_retrans = $P.tp_Nretrans; 888 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 889 } 890 } 891; 892 893TP_REFWAIT <== [ TP_OPEN, TP_CRSENT ] T_DISC_req 894 ( $P.tp_class == TP_CLASS_0 ) 895 { 896 tp_soisdisconnecting($P.tp_sock); 897 tp_netcmd( $P, CONN_CLOSE); 898 tp_soisdisconnected($P); 899 } 900; 901 902/* TP4 only */ 903TP_CLOSING <== [ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ] T_DISC_req 904 DEFAULT 905 { 906 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 907 908 if($P.tp_state == TP_OPEN) { 909 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */ 910 tp_cuntimeout($P.tp_refp, TM_inact); 911 tp_cuntimeout($P.tp_refp, TM_sendack); 912 } 913 if (data) { 914 IFDEBUG(D_CONN) 915 printf("T_DISC_req.trans tp_ucddata 0x%x\n", 916 $P.tp_ucddata); 917 dump_mbuf(data, "ucddata @ T_DISC_req"); 918 ENDDEBUG 919 } 920 tp_soisdisconnecting($P.tp_sock); 921 $P.tp_retrans = $P.tp_Nretrans; 922 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 923 924 if( trick_hc ) 925 return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data); 926 } 927; 928 929/* TP4 only */ 930SAME <== TP_AKWAIT TM_retrans 931 ( $P.tp_retrans > 0 ) 932 { 933 int error; 934 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 935 936 IncStat(ts_retrans_cc); 937 $P.tp_retrans --; 938 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) 939 $P.tp_sock->so_error = error; 940 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks); 941 } 942; 943 944/* TP4 only */ 945TP_CLOSING <== TP_AKWAIT TM_retrans 946 DEFAULT /* out of time */ 947 { 948 IncStat(ts_conn_gaveup); 949 tp_soisdisconnecting($P.tp_sock); 950 $P.tp_sock->so_error = ETIMEDOUT; 951 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 952 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL); 953 $P.tp_retrans = $P.tp_Nretrans; 954 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 955 } 956; 957 958/* the retrans timers had better go off BEFORE the inactivity timer does, 959 * if transmissions are going on. 960 * (i.e., TM_inact should be greater than timer for all retrans plus ack 961 * turnaround) 962 */ 963/* TP4 only */ 964TP_CLOSING <== TP_OPEN [ TM_inact, TM_retrans, TM_data_retrans ] 965 DEFAULT 966 { 967 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */ 968 tp_cuntimeout($P.tp_refp, TM_inact); 969 tp_cuntimeout($P.tp_refp, TM_sendack); 970 971 IncStat(ts_conn_gaveup); 972 tp_soisdisconnecting($P.tp_sock); 973 $P.tp_sock->so_error = ETIMEDOUT; 974 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 975 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL); 976 $P.tp_retrans = $P.tp_Nretrans; 977 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 978 } 979; 980 981/* TP4 only */ 982SAME <== TP_OPEN TM_retrans 983 ( $P.tp_retrans > 0 ) 984 { 985 /* resume XPD */ 986 if ( $P.tp_Xsnd.sb_mb ) { 987 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); 988 /* m_copy doesn't preserve the m_xlink field, but at this pt. 989 * that doesn't matter 990 */ 991 992 IFTRACE(D_XPD) 993 tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndhiwat snduna", 994 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat, 995 $P.tp_snduna); 996 ENDTRACE 997 IFDEBUG(D_XPD) 998 dump_mbuf(m, "XPD retrans emitting M"); 999 ENDDEBUG 1000 IncStat(ts_retrans_xpd); 1001 $P.tp_retrans --; 1002 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1003 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks); 1004 } 1005 } 1006; 1007 1008/* TP4 only */ 1009SAME <== TP_OPEN TM_data_retrans 1010 ( $$.e_retrans > 0 ) 1011 { 1012 register SeqNum low, lowsave = 0; 1013 register struct tp_rtc *r = $P.tp_snduna_rtc; 1014 register struct mbuf *m; 1015 register SeqNum high = $$.e_high; 1016 1017 low = 1018 SEQ_GT($P, $P.tp_snduna, $$.e_low )? $P.tp_snduna: $$.e_low; 1019 lowsave = low; 1020 if (($P.tp_rx_strat & TPRX_EACH) == 0) 1021 high = (high>low)?low:high; 1022 1023 if( $P.tp_rx_strat & TPRX_USE_CW ) { 1024 register int i; 1025 1026 $P.tp_cong_win = 1; 1027 1028 i = SEQ_ADD($P, low, $P.tp_cong_win); 1029 if(SEQ_LT($P, i, high )) 1030 high = i; 1031 } 1032 1033 while( SEQ_LEQ($P, low, high) ){ 1034 if ( r == (struct tp_rtc *)0 ){ 1035 IFDEBUG(D_RTC) 1036 printf( "tp: retrans rtc list is GONE!\n"); 1037 ENDDEBUG 1038 break; 1039 } 1040 if ( r->tprt_seq == low ){ 1041 if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))== MNULL) 1042 break; 1043 (void) tp_emit(DT_TPDU_type, $P, low, r->tprt_eot, m); 1044 IncStat(ts_retrans_dt); 1045 SEQ_INC($P, low ); 1046 } 1047 r = r->tprt_next; 1048 } 1049 if ( SEQ_LEQ($P, lowsave, high) ){ 1050 $$.e_retrans --; 1051 tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)lowsave, 1052 (caddr_t)high, $$.e_retrans, 1053 ($P.tp_Nretrans - $$.e_retrans) * (int)$P.tp_dt_ticks); 1054 } 1055 } 1056; 1057 1058/* TP4 only */ 1059SAME <== TP_CLOSING TM_retrans 1060 ( $P.tp_retrans > 0 ) 1061 { 1062 $P.tp_retrans --; 1063 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL); 1064 IncStat(ts_retrans_dr); 1065 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 1066 } 1067; 1068 1069/* TP4 only */ 1070TP_REFWAIT <== TP_CLOSING TM_retrans 1071 DEFAULT /* no more retrans - gave up */ 1072 { 1073 $P.tp_sock->so_error = ETIMEDOUT; 1074 $P.tp_refp->tpr_state = REF_FROZEN; 1075 tp_recycle_tsuffix( $P ); 1076 tp_etimeout($P.tp_refp, TM_reference, 0,0,0, (int)$P.tp_refer_ticks); 1077 } 1078; 1079 1080/* 1081 * The resources are kept around until the ref timer goes off. 1082 * The suffices are wiped out sooner so they can be reused right away. 1083 */ 1084/* applicable in TP4, TP0 */ 1085TP_CLOSED <== TP_REFWAIT TM_reference 1086 DEFAULT 1087 { 1088 tp_freeref($P.tp_refp); 1089 tp_detach($P); 1090 } 1091; 1092 1093/* applicable in TP4, TP0 */ 1094/* A duplicate CR from connectionless network layer can't happen */ 1095SAME <== TP_OPEN [ CR_TPDU, CC_TPDU ] 1096 DEFAULT 1097 { 1098 if( $P.tp_class != TP_CLASS_0) { 1099 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1100 if ( $E.ev_number == CC_TPDU ) 1101 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1102 } 1103 /* ignore it if class 0 - state tables are blank for this */ 1104 } 1105; 1106 1107/* applicable in TP4, TP0 */ 1108SAME <== TP_OPEN T_DATA_req 1109 DEFAULT 1110 { 1111 IFTRACE(D_DATA) 1112 tptrace(TPPTmisc, "T_DATA_req sndhiwat snduna fcredit, tpcb", 1113 $P.tp_sndhiwat, $P.tp_snduna, $P.tp_fcredit, $P); 1114 ENDTRACE 1115 1116 tp_send($P); 1117 } 1118; 1119 1120/* TP4 only */ 1121SAME <== TP_OPEN T_XPD_req 1122 DEFAULT 1123 /* T_XPD_req was issued by sosend iff xpd socket buf was empty 1124 * at time of sosend(), 1125 * AND (which means) there were no unacknowledged XPD tpdus outstanding! 1126 */ 1127 { 1128 int error = 0; 1129 1130 /* resume XPD */ 1131 if ( $P.tp_Xsnd.sb_mb ) { 1132 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); 1133 /* m_copy doesn't preserve the m_xlink field, but at this pt. 1134 * that doesn't matter 1135 */ 1136 1137 IFTRACE(D_XPD) 1138 tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndhiwat snduna", 1139 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat, 1140 $P.tp_snduna); 1141 ENDTRACE 1142 IFDEBUG(D_XPD) 1143 printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc); 1144 dump_mbuf(m, "XPD req emitting M"); 1145 ENDDEBUG 1146 error = 1147 tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1148 $P.tp_retrans = $P.tp_Nretrans; 1149 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks); 1150 SEQ_INC($P, $P.tp_Xsndnxt); 1151 } 1152 if(trick_hc) 1153 return error; 1154 } 1155; 1156 1157/* TP4, faked ack in TP0 when cons send completes */ 1158SAME <== TP_OPEN AK_TPDU 1159 ( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq) ) 1160 1161 /* tp_goodack == true means 1162 * EITHER it actually acked something heretofore unacknowledged 1163 * OR no news but the credit should be processed. 1164 */ 1165 { 1166 IFDEBUG(D_ACKRECV) 1167 printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt); 1168 ENDDEBUG 1169 if( $P.tp_class != TP_CLASS_0) { 1170 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1171 tp_euntimeout_lss($P.tp_refp, TM_data_retrans, $$.e_seq); 1172 } 1173 sbwakeup( &$P.tp_sock->so_snd ); 1174 1175 tp_send($P); 1176 IFDEBUG(D_ACKRECV) 1177 printf("GOOD ACK new sndhiwat 0x%x\n", $P.tp_sndhiwat); 1178 ENDDEBUG 1179 } 1180; 1181 1182/* TP4, and TP0 after sending a CC or possibly a CR */ 1183SAME <== TP_OPEN XAK_TPDU 1184 DEFAULT 1185 { 1186 IFTRACE(D_ACKRECV) 1187 tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0); 1188 ENDTRACE 1189 if( $P.tp_class != TP_CLASS_0 ) { 1190 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1191 } 1192 } 1193; 1194/* TP4, and TP0 after sending a CC or possibly a CR */ 1195SAME <== TP_OPEN AK_TPDU 1196 DEFAULT 1197 { 1198 IFTRACE(D_ACKRECV) 1199 tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq", 1200 $$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0); 1201 ENDTRACE 1202 if( $P.tp_class != TP_CLASS_0 ) { 1203 1204 if ( !$$.e_fcc_present ) { 1205 /* send ACK with FCC */ 1206 IncStat( ts_ackreason[_ACK_FCC_] ); 1207 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL); 1208 } 1209 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1210 } 1211 } 1212; 1213 1214/* NBS(47) */ 1215 /* goes in at *** */ 1216 /* just so happens that this is never true now, because we allow 1217 * only 1 packet in the queue at once (this could be changed) 1218 if ( $P.tp_Xsnd.sb_mb ) { 1219 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??); 1220 1221 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1222 $P.tp_retrans = $P.tp_Nretrans; 1223 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks); 1224 SEQ_INC($P, $P.tp_Xsndnxt); 1225 } 1226 */ 1227 /* end of the above hack */ 1228 1229/* TP4 only */ 1230SAME <== TP_OPEN XAK_TPDU 1231 ( tp_goodXack($P, $$.e_seq) ) 1232 /* tp_goodXack checks for good ack, removes the correct 1233 * tpdu from the queue and returns 1 if ack was legit, 0 if not. 1234 * also updates tp_Xuna 1235 */ 1236 { 1237 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1238 tp_cuntimeout($P.tp_refp, TM_retrans); 1239 1240 sbwakeup( &$P.tp_sock->so_snd ); 1241 1242 /* resume normal data */ 1243 tp_send($P); 1244 } 1245; 1246 1247/* TP4 only */ 1248SAME <== TP_OPEN TM_sendack 1249 DEFAULT 1250 { 1251 IFTRACE(D_TIMER) 1252 tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe, 1253 $P.tp_sent_lcdt, 0); 1254 ENDTRACE 1255 IncPStat($P, tps_n_TMsendack); 1256 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1257 } 1258; 1259 1260/* TP0 only */ 1261SAME <== TP_OPEN T_USR_rcvd 1262 ($P.tp_class == TP_CLASS_0) 1263 NULLACTION 1264; 1265 1266/* TP4 only */ 1267 /* If old credit was zero, 1268 * we'd better inform other side that we now have space 1269 * But this is not enough. Sender might not yet have 1270 * seen an ack with cdt 0 but it might still think the 1271 * window is closed, so it's going to wait. 1272 * Best to send an ack each time. 1273 * Strictly speaking, this ought to be a function of the 1274 * general ack strategy. 1275 */ 1276SAME <== TP_OPEN T_USR_rcvd 1277 DEFAULT 1278 { 1279 if( trick_hc ) { 1280 IncStat(ts_ackreason[_ACK_USRRCV_]); 1281 return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1282 } 1283 } 1284; 1285 1286/* applicable in TP4, TP0 */ 1287SAME <== TP_REFWAIT [ T_USR_rcvd, T_USR_Xrcvd ] 1288 DEFAULT 1289 /* This happens if other end sent a DR when the user was waiting 1290 * on a receive. 1291 * Processing the DR includes putting us in REFWAIT state. 1292 */ 1293 { 1294 if(trick_hc) 1295 return ECONNABORTED; 1296 } 1297; 1298 1299/* TP0 only */ 1300TP_REFWAIT <== [ TP_OPEN, TP_CRSENT, TP_LISTENING ] T_NETRESET 1301 ( $P.tp_class != TP_CLASS_4 ) 1302 /* 0 or (4 and 0) */ 1303 /* in OPEN class will be 0 or 4 but not both */ 1304 /* in CRSENT or LISTENING it could be in negotiation, hence both */ 1305 /* Actually, this shouldn't ever happen in LISTENING */ 1306 { 1307 ASSERT( $P.tp_state != TP_LISTENING ); 1308 tp_indicate(T_DISCONNECT, $P, ECONNRESET); 1309 tp_soisdisconnected($P); 1310 } 1311; 1312 1313/* TP4: ignore resets */ 1314SAME <== [ TP_OPEN, TP_CRSENT, TP_AKWAIT, 1315 TP_CLOSING, TP_LISTENING ] T_NETRESET 1316 DEFAULT 1317 NULLACTION 1318; 1319 1320/* applicable in TP4, TP0 */ 1321SAME <== [ TP_CLOSED, TP_REFWAIT ] T_NETRESET 1322 DEFAULT 1323 NULLACTION 1324; 1325 1326/* C'EST TOUT */ 1327