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