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