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