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