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