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