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