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