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.19 (Berkeley) 05/27/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.19 (Berkeley) 05/27/92 */ 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 $P.tp_flags &= ~TPF_DELACK; 439 } 440 tp_cuntimeout($P, TM_retrans); 441 if( $$.e_sref != 0 ) 442 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); 443 } 444 } 445; 446 447SAME <== TP_CLOSED DR_TPDU 448 DEFAULT 449 { 450 if( $$.e_sref != 0 ) 451 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); 452 /* reference timer already set - reset it to be safe (???) */ 453 tp_euntimeout($P, TM_reference); /* all */ 454 tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks); 455 } 456; 457 458/* NBS(34) */ 459TP_REFWAIT <== TP_CRSENT ER_TPDU 460 DEFAULT 461 { 462 tp_cuntimeout($P, TM_retrans); 463 tp_indicate(ER_TPDU, $P, $$.e_reason); 464 tp_soisdisconnected($P); 465 } 466; 467 468/* NBS(27) */ 469TP_REFWAIT <== TP_CLOSING DR_TPDU 470 DEFAULT 471 { 472 tp_cuntimeout($P, TM_retrans); 473 tp_soisdisconnected($P); 474 } 475; 476/* these two transitions are the same but can't be combined because xebec 477 * can't handle the use of $$.e_reason if they're combined 478 */ 479/* NBS(27) */ 480TP_REFWAIT <== TP_CLOSING ER_TPDU 481 DEFAULT 482 { 483 tp_indicate(ER_TPDU, $P, $$.e_reason); 484 tp_cuntimeout($P, TM_retrans); 485 tp_soisdisconnected($P); 486 } 487; 488/* NBS(27) */ 489TP_REFWAIT <== TP_CLOSING DC_TPDU 490 DEFAULT 491 { 492 tp_cuntimeout($P, TM_retrans); 493 tp_soisdisconnected($P); 494 } 495; 496 497/* NBS(21) */ 498SAME <== TP_CLOSED [ CC_TPDU, CR_TPDU ] 499 DEFAULT 500 { /* don't ask me why we have to do this - spec says so */ 501 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL); 502 /* don't bother with retransmissions of the DR */ 503 } 504; 505 506/* NBS(34) */ 507TP_REFWAIT <== TP_OPEN ER_TPDU 508 ($P.tp_class == TP_CLASS_0) 509 { 510 tp_soisdisconnecting($P.tp_sock); 511 tp_indicate(ER_TPDU, $P, $$.e_reason); 512 tp_soisdisconnected($P); 513 tp_netcmd( $P, CONN_CLOSE ); 514 } 515; 516 517TP_CLOSING <== [ TP_AKWAIT, TP_OPEN ] ER_TPDU 518 DEFAULT 519 { 520 if ($P.tp_state == TP_OPEN) { 521 tp_euntimeout($P, TM_data_retrans); /* all */ 522 tp_cuntimeout($P, TM_inact); 523 tp_cuntimeout($P, TM_sendack); 524 } 525 tp_soisdisconnecting($P.tp_sock); 526 tp_indicate(ER_TPDU, $P, $$.e_reason); 527 $P.tp_retrans = $P.tp_Nretrans; 528 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 529 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL); 530 } 531; 532/* NBS(6) */ 533TP_OPEN <== TP_CRSENT CC_TPDU 534 ($P.tp_class == TP_CLASS_0) 535 { 536 tp_cuntimeout($P, TM_retrans); 537 IncStat(ts_tp0_conn); 538 $P.tp_fcredit = 1; 539 soisconnected($P.tp_sock); 540 } 541; 542 543TP_OPEN <== TP_CRSENT CC_TPDU 544 DEFAULT 545 { 546 IFDEBUG(D_CONN) 547 printf("trans: CC_TPDU in CRSENT state flags 0x%x\n", 548 (int)$P.tp_flags); 549 ENDDEBUG 550 IncStat(ts_tp4_conn); 551 $P.tp_fref = $$.e_sref; 552 $P.tp_fcredit = $$.e_cdt; 553 if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0)) 554 $P.tp_cong_win = $$.e_cdt * $P.tp_l_tpdusize; 555 tp_getoptions($P); 556 tp_cuntimeout($P, TM_retrans); 557 if ($P.tp_ucddata) { 558 IFDEBUG(D_CONN) 559 printf("dropping user connect data cc 0x%x\n", 560 $P.tp_ucddata->m_len); 561 ENDDEBUG 562 m_freem($P.tp_ucddata); 563 $P.tp_ucddata = 0; 564 } 565 soisconnected($P.tp_sock); 566 if ($$.e_datalen > 0) { 567 ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */ 568 sbappendrecord(&$P.tp_Xrcv, $$.e_data); 569 $$.e_data = MNULL; 570 } 571 572 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 573 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 574 } 575; 576 577/* TP4 only */ 578SAME <== TP_CRSENT TM_retrans 579 ( $P.tp_retrans > 0 ) 580 { 581 struct mbuf *data = MNULL; 582 int error; 583 584 IncStat(ts_retrans_cr); 585 $P.tp_cong_win = 1 * $P.tp_l_tpdusize; 586 data = MCPY($P.tp_ucddata, M_NOWAIT); 587 if($P.tp_ucddata) { 588 IFDEBUG(D_CONN) 589 printf("TM_retrans.trans m_copy cc 0x%x\n", data); 590 dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans"); 591 ENDDEBUG 592 if( data == MNULL ) 593 return ENOBUFS; 594 } 595 596 $P.tp_retrans --; 597 if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) { 598 $P.tp_sock->so_error = error; 599 } 600 tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks); 601 } 602; 603 604/* TP4 only */ 605TP_REFWAIT <== TP_CRSENT TM_retrans 606 DEFAULT /* no more CR retransmissions */ 607 { 608 IncStat(ts_conn_gaveup); 609 $P.tp_sock->so_error = ETIMEDOUT; 610 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 611 tp_soisdisconnected($P); 612 } 613; 614 615/* TP4 only */ 616SAME <== TP_AKWAIT CR_TPDU 617 DEFAULT 618 /* duplicate CR (which doesn't really exist in the context of 619 * a connectionless network layer) 620 * Doesn't occur in class 0. 621 */ 622 { 623 int error; 624 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 625 626 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) { 627 $P.tp_sock->so_error = error; 628 } 629 $P.tp_retrans = $P.tp_Nretrans; 630 tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks); 631 } 632; 633 634/* TP4 only */ 635TP_OPEN <== TP_AKWAIT DT_TPDU 636 ( IN_RWINDOW( $P, $$.e_seq, 637 $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) ) 638 { 639 int doack; 640 641 /* 642 * Get rid of any confirm or connect data, so that if we 643 * crash or close, it isn't thought of as disconnect data. 644 */ 645 if ($P.tp_ucddata) { 646 m_freem($P.tp_ucddata); 647 $P.tp_ucddata = 0; 648 } 649 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 650 tp_cuntimeout($P, TM_retrans); 651 soisconnected($P.tp_sock); 652 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 653 654 /* see also next 2 transitions, if you make any changes */ 655 656 doack = tp_stash($P, $E); 657 IFDEBUG(D_DATA) 658 printf("tp_stash returns %d\n",doack); 659 ENDDEBUG 660 661 if (doack) { 662 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); 663 tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks); 664 } else 665 tp_ctimeout( $P, TM_sendack, (int)$P.tp_sendack_ticks); 666 667 IFDEBUG(D_DATA) 668 printf("after stash calling sbwakeup\n"); 669 ENDDEBUG 670 } 671; 672 673SAME <== TP_OPEN DT_TPDU 674 ( $P.tp_class == TP_CLASS_0 ) 675 { 676 tp0_stash($P, $E); 677 sbwakeup( &$P.tp_sock->so_rcv ); 678 679 IFDEBUG(D_DATA) 680 printf("after stash calling sbwakeup\n"); 681 ENDDEBUG 682 } 683; 684 685/* TP4 only */ 686SAME <== TP_OPEN DT_TPDU 687 ( IN_RWINDOW( $P, $$.e_seq, 688 $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) ) 689 { 690 int doack; /* tells if we must ack immediately */ 691 692 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 693 sbwakeup( &$P.tp_sock->so_rcv ); 694 695 doack = tp_stash($P, $E); 696 IFDEBUG(D_DATA) 697 printf("tp_stash returns %d\n",doack); 698 ENDDEBUG 699 700 if(doack) 701 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); 702 else 703 tp_ctimeout_MIN( $P, TM_sendack, (int)$P.tp_sendack_ticks); 704 705 IFDEBUG(D_DATA) 706 printf("after stash calling sbwakeup\n"); 707 ENDDEBUG 708 } 709; 710 711/* Not in window - we must ack under certain circumstances, namely 712 * a) if the seq number is below lwe but > lwe - (max credit ever given) 713 * (to handle lost acks) Can use max-possible-credit for this ^^^. 714 * and 715 * b) seq number is > uwe but < uwe + previously sent & withdrawn credit 716 * 717 * (see 12.2.3.8.1 of ISO spec, p. 73) 718 * We just always ack. 719 */ 720/* TP4 only */ 721SAME <== [ TP_OPEN, TP_AKWAIT ] DT_TPDU 722 DEFAULT /* Not in window */ 723 { 724 IFTRACE(D_DATA) 725 tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ", 726 $$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0); 727 ENDTRACE 728 IncStat(ts_dt_niw); 729 m_freem($$.e_data); 730 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 731 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); 732 } 733; 734 735/* TP4 only */ 736TP_OPEN <== TP_AKWAIT AK_TPDU 737 DEFAULT 738 { 739 if ($P.tp_ucddata) { 740 m_freem($P.tp_ucddata); 741 $P.tp_ucddata = 0; 742 } 743 (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq); 744 tp_cuntimeout($P, TM_retrans); 745 746 soisconnected($P.tp_sock); 747 IFTRACE(D_CONN) 748 struct socket *so = $P.tp_sock; 749 tptrace(TPPTmisc, 750 "called sosiconn: so so_state rcv.sb_sel rcv.sb_flags", 751 so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags); 752 tptrace(TPPTmisc, 753 "called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head", 754 so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head); 755 ENDTRACE 756 757 tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks); 758 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 759 } 760; 761 762/* TP4 only */ 763TP_OPEN <== [ TP_OPEN, TP_AKWAIT ] XPD_TPDU 764 ($P.tp_Xrcvnxt == $$.e_seq) 765 { 766 if( $P.tp_state == TP_AKWAIT ) { 767 if ($P.tp_ucddata) { 768 m_freem($P.tp_ucddata); 769 $P.tp_ucddata = 0; 770 } 771 tp_cuntimeout($P, TM_retrans); 772 soisconnected($P.tp_sock); 773 tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks); 774 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 775 } 776 IFTRACE(D_XPD) 777 tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n", 778 $P.tp_Xrcvnxt,$$.e_seq, $$.e_datalen, $$.e_data->m_len); 779 ENDTRACE 780 781 $P.tp_sock->so_state |= SS_RCVATMARK; 782 $$.e_data->m_flags |= M_EOR; 783 sbinsertoob(&$P.tp_Xrcv, $$.e_data); 784 IFDEBUG(D_XPD) 785 dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv"); 786 ENDDEBUG 787 tp_indicate(T_XDATA, $P, 0); 788 sbwakeup( &$P.tp_Xrcv ); 789 790 (void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL); 791 SEQ_INC($P, $P.tp_Xrcvnxt); 792 } 793; 794 795/* TP4 only */ 796SAME <== TP_OPEN T_USR_Xrcvd 797 DEFAULT 798 { 799 if( $P.tp_Xrcv.sb_cc == 0 ) { 800 /* kludge for select(): */ 801 /* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */ 802 } 803 } 804 /* OLD WAY: 805 * Ack only after the user receives the XPD. This is better for 806 * users that use one XPD right after another. 807 * Acking right away (the NEW WAY, see the prev. transition) is 808 * better for occasional * XPD, when the receiving user doesn't 809 * want to read the XPD immediately (which is session's behavior). 810 * 811 int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL); 812 SEQ_INC($P, $P.tp_Xrcvnxt); 813 return error; 814 */ 815; 816 817/* NOTE: presently if the user doesn't read the connection data 818 * before and expedited data PDU comes in, the connection data will 819 * be dropped. This is a bug. To avoid it, we need somewhere else 820 * to put the connection data. 821 * On the other hand, we need not to have it sitting around forever. 822 * This is a problem with the idea of trying to accommodate 823 * data on connect w/ a passive-open user interface. 824 */ 825/* TP4 only */ 826 827SAME <== [ TP_AKWAIT, TP_OPEN ] XPD_TPDU 828 DEFAULT /* not in window or cdt==0 */ 829 { 830 IFTRACE(D_XPD) 831 tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n", 832 $P.tp_Xrcvnxt, $$.e_seq, $P.tp_Xrcv.sb_cc , 0); 833 ENDTRACE 834 if( $P.tp_Xrcvnxt != $$.e_seq ) 835 IncStat(ts_xpd_niw); 836 if( $P.tp_Xrcv.sb_cc ) { 837 /* might as well kick 'em again */ 838 tp_indicate(T_XDATA, $P, 0); 839 IncStat(ts_xpd_dup); 840 } 841 m_freem($$.e_data); 842 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 843 /* don't send an xack because the xak gives "last one received", not 844 * "next one i expect" (dumb) 845 */ 846 } 847; 848 849/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries 850 * to detach all its "children" 851 * Also (CRSENT) when user kills a job that's doing a connect() 852 */ 853TP_REFWAIT <== TP_CRSENT T_DETACH 854 ($P.tp_class == TP_CLASS_0) 855 { 856 struct socket *so = $P.tp_sock; 857 858 /* detach from parent socket so it can finish closing */ 859 if (so->so_head) { 860 if (!soqremque(so, 0) && !soqremque(so, 1)) 861 panic("tp: T_DETACH"); 862 so->so_head = 0; 863 } 864 tp_soisdisconnecting($P.tp_sock); 865 tp_netcmd( $P, CONN_CLOSE); 866 tp_soisdisconnected($P); 867 } 868; 869 870/* TP4 only */ 871TP_CLOSING <== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ] T_DETACH 872 DEFAULT 873 { 874 struct socket *so = $P.tp_sock; 875 struct mbuf *data = MNULL; 876 877 /* detach from parent socket so it can finish closing */ 878 if (so->so_head) { 879 if (!soqremque(so, 0) && !soqremque(so, 1)) 880 panic("tp: T_DETACH"); 881 so->so_head = 0; 882 } 883 if ($P.tp_state != TP_CLOSING) { 884 tp_soisdisconnecting($P.tp_sock); 885 data = MCPY($P.tp_ucddata, M_NOWAIT); 886 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data); 887 $P.tp_retrans = $P.tp_Nretrans; 888 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 889 } 890 } 891; 892 893TP_REFWAIT <== [ TP_OPEN, TP_CRSENT ] T_DISC_req 894 ( $P.tp_class == TP_CLASS_0 ) 895 { 896 tp_soisdisconnecting($P.tp_sock); 897 tp_netcmd( $P, CONN_CLOSE); 898 tp_soisdisconnected($P); 899 } 900; 901 902/* TP4 only */ 903TP_CLOSING <== [ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ] T_DISC_req 904 DEFAULT 905 { 906 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 907 908 if($P.tp_state == TP_OPEN) { 909 tp_euntimeout($P, TM_data_retrans); /* all */ 910 tp_cuntimeout($P, TM_inact); 911 tp_cuntimeout($P, TM_sendack); 912 $P.tp_flags &= ~TPF_DELACK; 913 } 914 if (data) { 915 IFDEBUG(D_CONN) 916 printf("T_DISC_req.trans tp_ucddata 0x%x\n", 917 $P.tp_ucddata); 918 dump_mbuf(data, "ucddata @ T_DISC_req"); 919 ENDDEBUG 920 } 921 tp_soisdisconnecting($P.tp_sock); 922 $P.tp_retrans = $P.tp_Nretrans; 923 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 924 925 if( trick_hc ) 926 return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data); 927 } 928; 929 930/* TP4 only */ 931SAME <== TP_AKWAIT TM_retrans 932 ( $P.tp_retrans > 0 ) 933 { 934 int error; 935 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 936 937 IncStat(ts_retrans_cc); 938 $P.tp_retrans --; 939 $P.tp_cong_win = 1 * $P.tp_l_tpdusize; 940 941 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) 942 $P.tp_sock->so_error = error; 943 tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks); 944 } 945; 946 947/* TP4 only */ 948TP_CLOSING <== TP_AKWAIT TM_retrans 949 DEFAULT /* out of time */ 950 { 951 IncStat(ts_conn_gaveup); 952 tp_soisdisconnecting($P.tp_sock); 953 $P.tp_sock->so_error = ETIMEDOUT; 954 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 955 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL); 956 $P.tp_retrans = $P.tp_Nretrans; 957 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 958 } 959; 960 961/* the retrans timers had better go off BEFORE the inactivity timer does, 962 * if transmissions are going on. 963 * (i.e., TM_inact should be greater than timer for all retrans plus ack 964 * turnaround) 965 */ 966/* TP4 only */ 967TP_CLOSING <== TP_OPEN [ TM_inact, TM_retrans, TM_data_retrans ] 968 DEFAULT 969 { 970 tp_euntimeout($P, TM_data_retrans); /* all */ 971 tp_cuntimeout($P, TM_inact); 972 tp_cuntimeout($P, TM_sendack); 973 974 IncStat(ts_conn_gaveup); 975 tp_soisdisconnecting($P.tp_sock); 976 $P.tp_sock->so_error = ETIMEDOUT; 977 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 978 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL); 979 $P.tp_retrans = $P.tp_Nretrans; 980 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 981 } 982; 983 984/* TP4 only */ 985SAME <== TP_OPEN TM_retrans 986 ( $P.tp_retrans > 0 ) 987 { 988 $P.tp_cong_win = 1 * $P.tp_l_tpdusize; 989 /* resume XPD */ 990 if ( $P.tp_Xsnd.sb_mb ) { 991 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); 992 int shift; 993 994 IFTRACE(D_XPD) 995 tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna", 996 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt, 997 $P.tp_snduna); 998 ENDTRACE 999 IFDEBUG(D_XPD) 1000 dump_mbuf(m, "XPD retrans emitting M"); 1001 ENDDEBUG 1002 IncStat(ts_retrans_xpd); 1003 $P.tp_retrans --; 1004 shift = max($P.tp_Nretrans - $P.tp_retrans, 6); 1005 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1006 tp_ctimeout($P, TM_retrans, ((int)$P.tp_dt_ticks) << shift); 1007 } 1008 } 1009; 1010 1011/* TP4 only */ 1012SAME <== TP_OPEN TM_data_retrans 1013 ($P.tp_rxtshift < TP_NRETRANS) 1014 { 1015 $P.tp_rxtshift++; 1016 (void) tp_data_retrans($P); 1017 } 1018; 1019 1020/* TP4 only */ 1021SAME <== TP_CLOSING TM_retrans 1022 ( $P.tp_retrans > 0 ) 1023 { 1024 $P.tp_retrans --; 1025 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL); 1026 IncStat(ts_retrans_dr); 1027 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 1028 } 1029; 1030 1031/* TP4 only */ 1032TP_REFWAIT <== TP_CLOSING TM_retrans 1033 DEFAULT /* no more retrans - gave up */ 1034 { 1035 $P.tp_sock->so_error = ETIMEDOUT; 1036 $P.tp_refstate = REF_FROZEN; 1037 tp_recycle_tsuffix( $P ); 1038 tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks); 1039 } 1040; 1041 1042/* 1043 * The resources are kept around until the ref timer goes off. 1044 * The suffices are wiped out sooner so they can be reused right away. 1045 */ 1046/* applicable in TP4, TP0 */ 1047TP_CLOSED <== TP_REFWAIT TM_reference 1048 DEFAULT 1049 { 1050 tp_freeref($P.tp_lref); 1051 tp_detach($P); 1052 } 1053; 1054 1055/* applicable in TP4, TP0 */ 1056/* A duplicate CR from connectionless network layer can't happen */ 1057SAME <== TP_OPEN [ CR_TPDU, CC_TPDU ] 1058 DEFAULT 1059 { 1060 if( $P.tp_class != TP_CLASS_0) { 1061 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 1062 if ( $E.ev_number == CC_TPDU ) 1063 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1064 } 1065 /* ignore it if class 0 - state tables are blank for this */ 1066 } 1067; 1068 1069/* applicable in TP4, TP0 */ 1070SAME <== TP_OPEN T_DATA_req 1071 DEFAULT 1072 { 1073 IFTRACE(D_DATA) 1074 tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb", 1075 $P.tp_sndnxt, $P.tp_snduna, $P.tp_fcredit, $P); 1076 ENDTRACE 1077 1078 tp_send($P); 1079 } 1080; 1081 1082/* TP4 only */ 1083SAME <== TP_OPEN T_XPD_req 1084 DEFAULT 1085 /* T_XPD_req was issued by sosend iff xpd socket buf was empty 1086 * at time of sosend(), 1087 * AND (which means) there were no unacknowledged XPD tpdus outstanding! 1088 */ 1089 { 1090 int error = 0; 1091 1092 /* resume XPD */ 1093 if ( $P.tp_Xsnd.sb_mb ) { 1094 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); 1095 /* m_copy doesn't preserve the m_xlink field, but at this pt. 1096 * that doesn't matter 1097 */ 1098 1099 IFTRACE(D_XPD) 1100 tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna", 1101 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt, 1102 $P.tp_snduna); 1103 ENDTRACE 1104 IFDEBUG(D_XPD) 1105 printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc); 1106 dump_mbuf(m, "XPD req emitting M"); 1107 ENDDEBUG 1108 error = 1109 tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1110 $P.tp_retrans = $P.tp_Nretrans; 1111 1112 tp_ctimeout($P, TM_retrans, (int)$P.tp_rxtcur); 1113 SEQ_INC($P, $P.tp_Xsndnxt); 1114 } 1115 if(trick_hc) 1116 return error; 1117 } 1118; 1119 1120/* TP4, faked ack in TP0 when cons send completes */ 1121SAME <== TP_OPEN AK_TPDU 1122 ( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq) ) 1123 1124 /* tp_goodack == true means 1125 * EITHER it actually acked something heretofore unacknowledged 1126 * OR no news but the credit should be processed. 1127 */ 1128 { 1129 struct sockbuf *sb = &$P.tp_sock->so_snd; 1130 1131 IFDEBUG(D_ACKRECV) 1132 printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt); 1133 ENDDEBUG 1134 if( $P.tp_class != TP_CLASS_0) { 1135 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 1136 } 1137 sbwakeup(sb); 1138 IFDEBUG(D_ACKRECV) 1139 printf("GOOD ACK new sndnxt 0x%x\n", $P.tp_sndnxt); 1140 ENDDEBUG 1141 } 1142; 1143 1144/* TP4, and TP0 after sending a CC or possibly a CR */ 1145SAME <== TP_OPEN AK_TPDU 1146 DEFAULT 1147 { 1148 IFTRACE(D_ACKRECV) 1149 tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq", 1150 $$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0); 1151 ENDTRACE 1152 if( $P.tp_class != TP_CLASS_0 ) { 1153 1154 if ( !$$.e_fcc_present ) { 1155 /* send ACK with FCC */ 1156 IncStat( ts_ackreason[_ACK_FCC_] ); 1157 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL); 1158 } 1159 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 1160 } 1161 } 1162; 1163 1164/* NBS(47) */ 1165 /* goes in at *** */ 1166 /* just so happens that this is never true now, because we allow 1167 * only 1 packet in the queue at once (this could be changed) 1168 if ( $P.tp_Xsnd.sb_mb ) { 1169 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??); 1170 1171 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1172 $P.tp_retrans = $P.tp_Nretrans; 1173 tp_ctimeout($P, TM_retrans, (int)$P.tp_xpd_ticks); 1174 SEQ_INC($P, $P.tp_Xsndnxt); 1175 } 1176 */ 1177 /* end of the above hack */ 1178 1179/* TP4 only */ 1180SAME <== TP_OPEN XAK_TPDU 1181 ( tp_goodXack($P, $$.e_seq) ) 1182 /* tp_goodXack checks for good ack, removes the correct 1183 * tpdu from the queue and returns 1 if ack was legit, 0 if not. 1184 * also updates tp_Xuna 1185 */ 1186 { 1187 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 1188 tp_cuntimeout($P, TM_retrans); 1189 1190 sbwakeup( &$P.tp_sock->so_snd ); 1191 1192 /* resume normal data */ 1193 tp_send($P); 1194 } 1195; 1196 1197/* TP4, and TP0 after sending a CC or possibly a CR */ 1198SAME <== TP_OPEN XAK_TPDU 1199 DEFAULT 1200 { 1201 IFTRACE(D_ACKRECV) 1202 tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0); 1203 ENDTRACE 1204 if( $P.tp_class != TP_CLASS_0 ) { 1205 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 1206 } 1207 } 1208; 1209 1210/* TP4 only */ 1211SAME <== TP_OPEN TM_sendack 1212 DEFAULT 1213 { 1214 int timo; 1215 IFTRACE(D_TIMER) 1216 tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe, 1217 $P.tp_sent_lcdt, 0); 1218 ENDTRACE 1219 IncPStat($P, tps_n_TMsendack); 1220 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1221 if ($P.tp_fcredit == 0) { 1222 if ($P.tp_rxtshift < TP_MAXRXTSHIFT) 1223 $P.tp_rxtshift++; 1224 timo = ($P.tp_dt_ticks) << $P.tp_rxtshift; 1225 } else 1226 timo = $P.tp_sendack_ticks; 1227 tp_ctimeout($P, TM_sendack, timo); 1228 } 1229; 1230 1231/* TP0 only */ 1232SAME <== TP_OPEN T_USR_rcvd 1233 ($P.tp_class == TP_CLASS_0) 1234 { 1235 if (sbspace(&$P.tp_sock->so_rcv) > 0) 1236 tp0_openflow($P); 1237 } 1238; 1239 1240/* TP4 only */ 1241 /* If old credit was zero, 1242 * we'd better inform other side that we now have space 1243 * But this is not enough. Sender might not yet have 1244 * seen an ack with cdt 0 but it might still think the 1245 * window is closed, so it's going to wait. 1246 * Best to send an ack each time. 1247 * Strictly speaking, this ought to be a function of the 1248 * general ack strategy. 1249 */ 1250SAME <== TP_OPEN T_USR_rcvd 1251 DEFAULT 1252 { 1253 if( trick_hc ) { 1254 SeqNum ack_thresh; 1255 /* 1256 * If the upper window edge has advanced a reasonable 1257 * amount beyond what was known, send an ACK. 1258 * A reasonable amount is 2 packets, unless the max window 1259 * is only 1 or 2 packets, in which case we 1260 * should send an ack for any advance in the upper window edge. 1261 */ 1262 LOCAL_CREDIT($P); 1263 ack_thresh = SEQ_SUB($P, $P.tp_lcredit + $P.tp_rcvnxt, 1264 ($P.tp_maxlcredit > 2 ? 2 : 1)); 1265 if (SEQ_GT($P, ack_thresh, $P.tp_sent_uwe)) { 1266 IncStat(ts_ackreason[_ACK_USRRCV_]); 1267 $P.tp_flags &= ~TPF_DELACK; 1268 return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1269 } 1270 } 1271 } 1272; 1273 1274/* applicable in TP4, TP0 */ 1275SAME <== TP_REFWAIT [ T_USR_rcvd, T_USR_Xrcvd ] 1276 DEFAULT 1277 /* This happens if other end sent a DR when the user was waiting 1278 * on a receive. 1279 * Processing the DR includes putting us in REFWAIT state. 1280 */ 1281 { 1282 if(trick_hc) 1283 return ECONNABORTED; 1284 } 1285; 1286 1287/* TP0 only */ 1288TP_REFWAIT <== [ TP_OPEN, TP_CRSENT, TP_LISTENING ] T_NETRESET 1289 ( $P.tp_class != TP_CLASS_4 ) 1290 /* 0 or (4 and 0) */ 1291 /* in OPEN class will be 0 or 4 but not both */ 1292 /* in CRSENT or LISTENING it could be in negotiation, hence both */ 1293 /* Actually, this shouldn't ever happen in LISTENING */ 1294 { 1295 ASSERT( $P.tp_state != TP_LISTENING ); 1296 tp_indicate(T_DISCONNECT, $P, ECONNRESET); 1297 tp_soisdisconnected($P); 1298 } 1299; 1300 1301/* TP4: ignore resets */ 1302SAME <== [ TP_OPEN, TP_CRSENT, TP_AKWAIT, 1303 TP_CLOSING, TP_LISTENING ] T_NETRESET 1304 DEFAULT 1305 NULLACTION 1306; 1307 1308/* applicable in TP4, TP0 */ 1309SAME <== [ TP_CLOSED, TP_REFWAIT ] T_NETRESET 1310 DEFAULT 1311 NULLACTION 1312; 1313 1314/* C'EST TOUT */ 1315