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