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.10 (Berkeley) 08/14/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.10 (Berkeley) 08/14/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 soisconnecting($P.tp_sock); 359 if (($P.tp_rx_strat & TPRX_FASTSTART) && ($P.tp_fcredit > 0)) 360 $P.tp_cong_win = $P.tp_fcredit; 361 $P.tp_retrans = $P.tp_Nretrans; 362 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks); 363 } 364; 365 366/* TP4 only */ 367TP_CLOSED <== TP_CONFIRMING T_ACPT_req 368 DEFAULT /* emit failed */ 369 { 370 register struct tp_ref *r = $P.tp_refp; 371 372 IFDEBUG(D_CONN) 373 printf("event: CR_TPDU emit CC failed done " ); 374 ENDDEBUG 375 soisdisconnected($P.tp_sock); 376 tp_recycle_tsuffix( $P ); 377 tp_freeref(r); 378 tp_detach($P); 379 } 380; 381 382/* applicable in TP4, TP0 */ 383TP_CRSENT <== TP_CLOSED T_CONN_req 384 DEFAULT 385 { 386 int error; 387 struct mbuf *data = MNULL; 388 389 IFTRACE(D_CONN) 390 tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags, 391 $P.tp_ucddata, 0, 0); 392 ENDTRACE 393 data = MCPY($P.tp_ucddata, M_WAIT); 394 if (data) { 395 IFDEBUG(D_CONN) 396 printf("T_CONN_req.trans m_copy cc 0x%x\n", 397 $P.tp_ucddata); 398 dump_mbuf(data, "sosnd @ T_CONN_req"); 399 ENDDEBUG 400 } 401 402 if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) 403 return error; /* driver WON'T change state; will return error */ 404 405 $P.tp_refp->tpr_state = REF_OPEN; /* has timers */ 406 if($P.tp_class != TP_CLASS_0) { 407 $P.tp_retrans = $P.tp_Nretrans; 408 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cr_ticks); 409 } 410 } 411; 412 413/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */ 414TP_REFWAIT <== [ TP_CRSENT, TP_AKWAIT, TP_OPEN ] DR_TPDU 415 DEFAULT 416 { 417 sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */ 418 if ($$.e_datalen > 0) { 419 sbappendrecord(&$P.tp_Xrcv, $$.e_data); 420 $$.e_data = MNULL; 421 } 422 tp_indicate(T_DISCONNECT, $P, 0); 423 tp_soisdisconnected($P); 424 if ($P.tp_class != TP_CLASS_0) { 425 if ($P.tp_state == TP_OPEN ) { 426 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */ 427 tp_cuntimeout($P.tp_refp, TM_retrans); 428 tp_cuntimeout($P.tp_refp, TM_inact); 429 tp_cuntimeout($P.tp_refp, TM_sendack); 430 } 431 tp_cuntimeout($P.tp_refp, TM_retrans); 432 if( $$.e_sref != 0 ) 433 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); 434 } 435 } 436; 437 438SAME <== TP_CLOSED DR_TPDU 439 DEFAULT 440 { 441 if( $$.e_sref != 0 ) 442 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); 443 /* reference timer already set - reset it to be safe (???) */ 444 tp_euntimeout($P.tp_refp, TM_reference); /* all */ 445 tp_etimeout($P.tp_refp, TM_reference, 0, 0, 0, (int)$P.tp_refer_ticks); 446 } 447; 448 449/* NBS(34) */ 450TP_REFWAIT <== TP_CRSENT ER_TPDU 451 DEFAULT 452 { 453 tp_cuntimeout($P.tp_refp, TM_retrans); 454 tp_indicate(ER_TPDU, $P, $$.e_reason); 455 tp_soisdisconnected($P); 456 } 457; 458 459/* NBS(27) */ 460TP_REFWAIT <== TP_CLOSING DR_TPDU 461 DEFAULT 462 { 463 tp_cuntimeout($P.tp_refp, TM_retrans); 464 tp_soisdisconnected($P); 465 } 466; 467/* these two transitions are the same but can't be combined because xebec 468 * can't handle the use of $$.e_reason if they're combined 469 */ 470/* NBS(27) */ 471TP_REFWAIT <== TP_CLOSING ER_TPDU 472 DEFAULT 473 { 474 tp_indicate(ER_TPDU, $P, $$.e_reason); 475 tp_cuntimeout($P.tp_refp, TM_retrans); 476 tp_soisdisconnected($P); 477 } 478; 479/* NBS(27) */ 480TP_REFWAIT <== TP_CLOSING DC_TPDU 481 DEFAULT 482 { 483 tp_cuntimeout($P.tp_refp, TM_retrans); 484 tp_soisdisconnected($P); 485 } 486; 487 488/* NBS(21) */ 489SAME <== TP_CLOSED [ CC_TPDU, CR_TPDU ] 490 DEFAULT 491 { /* don't ask me why we have to do this - spec says so */ 492 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL); 493 /* don't bother with retransmissions of the DR */ 494 } 495; 496 497/* NBS(34) */ 498TP_REFWAIT <== TP_OPEN ER_TPDU 499 ($P.tp_class == TP_CLASS_0) 500 { 501 tp_soisdisconnecting($P.tp_sock); 502 tp_indicate(ER_TPDU, $P, $$.e_reason); 503 tp_soisdisconnected($P); 504 tp_netcmd( $P, CONN_CLOSE ); 505 } 506; 507 508TP_CLOSING <== [ TP_AKWAIT, TP_OPEN ] ER_TPDU 509 DEFAULT 510 { 511 if ($P.tp_state == TP_OPEN) { 512 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */ 513 tp_cuntimeout($P.tp_refp, TM_inact); 514 tp_cuntimeout($P.tp_refp, TM_sendack); 515 } 516 tp_soisdisconnecting($P.tp_sock); 517 tp_indicate(ER_TPDU, $P, $$.e_reason); 518 $P.tp_retrans = $P.tp_Nretrans; 519 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 520 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL); 521 } 522; 523/* NBS(6) */ 524TP_OPEN <== TP_CRSENT CC_TPDU 525 ($P.tp_class == TP_CLASS_0) 526 { 527 tp_cuntimeout($P.tp_refp, TM_retrans); 528 IncStat(ts_tp0_conn); 529 $P.tp_fcredit = 1; 530 soisconnected($P.tp_sock); 531 } 532; 533 534TP_OPEN <== TP_CRSENT CC_TPDU 535 DEFAULT 536 { 537 IFDEBUG(D_CONN) 538 printf("trans: CC_TPDU in CRSENT state flags 0x%x\n", 539 (int)$P.tp_flags); 540 ENDDEBUG 541 IncStat(ts_tp4_conn); 542 $P.tp_fref = $$.e_sref; 543 $P.tp_fcredit = $$.e_cdt; 544 $P.tp_ackrcvd = 0; 545 if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0)) 546 $P.tp_cong_win = $$.e_cdt; 547 tp_getoptions($P); 548 tp_cuntimeout($P.tp_refp, TM_retrans); 549 if ($P.tp_ucddata) { 550 IFDEBUG(D_CONN) 551 printf("dropping user connect data cc 0x%x\n", 552 $P.tp_ucddata->m_len); 553 ENDDEBUG 554 m_freem($P.tp_ucddata); 555 $P.tp_ucddata = 0; 556 } 557 soisconnected($P.tp_sock); 558 if ($$.e_datalen > 0) { 559 ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */ 560 sbappendrecord(&$P.tp_Xrcv, $$.e_data); 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) 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 $$.e_data->m_flags |= M_EOR; 779 sbinsertoob(&$P.tp_Xrcv, $$.e_data); 780 IFDEBUG(D_XPD) 781 dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv"); 782 ENDDEBUG 783 tp_indicate(T_XDATA, $P, 0); 784 sbwakeup( &$P.tp_Xrcv ); 785 786 (void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL); 787 SEQ_INC($P, $P.tp_Xrcvnxt); 788 } 789; 790 791/* TP4 only */ 792SAME <== TP_OPEN T_USR_Xrcvd 793 DEFAULT 794 { 795 if( $P.tp_Xrcv.sb_cc == 0 ) { 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 /* might as well kick 'em again */ 834 tp_indicate(T_XDATA, $P, 0); 835 IncStat(ts_xpd_dup); 836 } 837 m_freem($$.e_data); 838 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 839 /* don't send an xack because the xak gives "last one received", not 840 * "next one i expect" (dumb) 841 */ 842 } 843; 844 845/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries 846 * to detach all its "children" 847 * Also (CRSENT) when user kills a job that's doing a connect() 848 */ 849TP_REFWAIT <== TP_CRSENT T_DETACH 850 ($P.tp_class == TP_CLASS_0) 851 { 852 struct socket *so = $P.tp_sock; 853 854 /* detach from parent socket so it can finish closing */ 855 if (so->so_head) { 856 if (!soqremque(so, 0) && !soqremque(so, 1)) 857 panic("tp: T_DETACH"); 858 so->so_head = 0; 859 } 860 tp_soisdisconnecting($P.tp_sock); 861 tp_netcmd( $P, CONN_CLOSE); 862 tp_soisdisconnected($P); 863 } 864; 865 866/* TP4 only */ 867TP_CLOSING <== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ] T_DETACH 868 DEFAULT 869 { 870 struct socket *so = $P.tp_sock; 871 struct mbuf *data = MNULL; 872 873 /* detach from parent socket so it can finish closing */ 874 if (so->so_head) { 875 if (!soqremque(so, 0) && !soqremque(so, 1)) 876 panic("tp: T_DETACH"); 877 so->so_head = 0; 878 } 879 if ($P.tp_state != TP_CLOSING) { 880 tp_soisdisconnecting($P.tp_sock); 881 data = MCPY($P.tp_ucddata, M_NOWAIT); 882 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data); 883 $P.tp_retrans = $P.tp_Nretrans; 884 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 885 } 886 } 887; 888 889TP_REFWAIT <== [ TP_OPEN, TP_CRSENT ] T_DISC_req 890 ( $P.tp_class == TP_CLASS_0 ) 891 { 892 tp_soisdisconnecting($P.tp_sock); 893 tp_netcmd( $P, CONN_CLOSE); 894 tp_soisdisconnected($P); 895 } 896; 897 898/* TP4 only */ 899TP_CLOSING <== [ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ] T_DISC_req 900 DEFAULT 901 { 902 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 903 904 if($P.tp_state == TP_OPEN) { 905 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */ 906 tp_cuntimeout($P.tp_refp, TM_inact); 907 tp_cuntimeout($P.tp_refp, TM_sendack); 908 } 909 if (data) { 910 IFDEBUG(D_CONN) 911 printf("T_DISC_req.trans tp_ucddata 0x%x\n", 912 $P.tp_ucddata); 913 dump_mbuf(data, "ucddata @ T_DISC_req"); 914 ENDDEBUG 915 } 916 tp_soisdisconnecting($P.tp_sock); 917 $P.tp_retrans = $P.tp_Nretrans; 918 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 919 920 if( trick_hc ) 921 return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data); 922 } 923; 924 925/* TP4 only */ 926SAME <== TP_AKWAIT TM_retrans 927 ( $P.tp_retrans > 0 ) 928 { 929 int error; 930 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 931 932 IncStat(ts_retrans_cc); 933 $P.tp_retrans --; 934 $P.tp_cong_win = 1; 935 $P.tp_ackrcvd = 0; 936 937 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) 938 $P.tp_sock->so_error = error; 939 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_cc_ticks); 940 } 941; 942 943/* TP4 only */ 944TP_CLOSING <== TP_AKWAIT TM_retrans 945 DEFAULT /* out of time */ 946 { 947 IncStat(ts_conn_gaveup); 948 tp_soisdisconnecting($P.tp_sock); 949 $P.tp_sock->so_error = ETIMEDOUT; 950 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 951 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL); 952 $P.tp_retrans = $P.tp_Nretrans; 953 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 954 } 955; 956 957/* the retrans timers had better go off BEFORE the inactivity timer does, 958 * if transmissions are going on. 959 * (i.e., TM_inact should be greater than timer for all retrans plus ack 960 * turnaround) 961 */ 962/* TP4 only */ 963TP_CLOSING <== TP_OPEN [ TM_inact, TM_retrans, TM_data_retrans ] 964 DEFAULT 965 { 966 tp_euntimeout($P.tp_refp, TM_data_retrans); /* all */ 967 tp_cuntimeout($P.tp_refp, TM_inact); 968 tp_cuntimeout($P.tp_refp, TM_sendack); 969 970 IncStat(ts_conn_gaveup); 971 tp_soisdisconnecting($P.tp_sock); 972 $P.tp_sock->so_error = ETIMEDOUT; 973 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 974 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL); 975 $P.tp_retrans = $P.tp_Nretrans; 976 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 977 } 978; 979 980/* TP4 only */ 981SAME <== TP_OPEN TM_retrans 982 ( $P.tp_retrans > 0 ) 983 { 984 $P.tp_cong_win = 1; 985 $P.tp_ackrcvd = 0; 986 /* resume XPD */ 987 if ( $P.tp_Xsnd.sb_mb ) { 988 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); 989 /* m_copy doesn't preserve the m_xlink field, but at this pt. 990 * that doesn't matter 991 */ 992 993 IFTRACE(D_XPD) 994 tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndhiwat snduna", 995 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat, 996 $P.tp_snduna); 997 ENDTRACE 998 IFDEBUG(D_XPD) 999 dump_mbuf(m, "XPD retrans emitting M"); 1000 ENDDEBUG 1001 IncStat(ts_retrans_xpd); 1002 $P.tp_retrans --; 1003 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1004 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks); 1005 } 1006 } 1007; 1008 1009/* TP4 only */ 1010SAME <== TP_OPEN TM_data_retrans 1011 ( $$.e_retrans > 0 ) 1012 { 1013 register SeqNum low, lowsave = 0; 1014 register struct tp_rtc *r = $P.tp_snduna_rtc; 1015 register struct mbuf *m; 1016 register SeqNum high = $$.e_high; 1017 1018 low = $P.tp_snduna; 1019 lowsave = high = low; 1020 1021 tp_euntimeout_lss($P.tp_refp, TM_data_retrans, 1022 SEQ_ADD($P, $P.tp_sndhiwat, 1)); 1023 $P.tp_retrans_hiwat = $P.tp_sndhiwat; 1024 1025 if (($P.tp_rx_strat & TPRX_EACH) == 0) 1026 high = (high>low)?low:high; 1027 1028 if( $P.tp_rx_strat & TPRX_USE_CW ) { 1029 register int i; 1030 1031 $P.tp_cong_win = 1; 1032 $P.tp_ackrcvd = 0; 1033 i = SEQ_ADD($P, low, $P.tp_cong_win); 1034 1035 high = SEQ_MIN($P, high, $P.tp_sndhiwat); 1036 1037 } 1038 1039 while( SEQ_LEQ($P, low, high) ){ 1040 if ( r == (struct tp_rtc *)0 ){ 1041 IFDEBUG(D_RTC) 1042 printf( "tp: retrans rtc list is GONE!\n"); 1043 ENDDEBUG 1044 break; 1045 } 1046 if ( r->tprt_seq == low ){ 1047 if(( m = m_copy(r->tprt_data, 0, r->tprt_octets ))== MNULL) 1048 break; 1049 (void) tp_emit(DT_TPDU_type, $P, low, r->tprt_eot, m); 1050 IncStat(ts_retrans_dt); 1051 SEQ_INC($P, low ); 1052 } 1053 r = r->tprt_next; 1054 } 1055/* CE_BIT 1056 if ( SEQ_LEQ($P, lowsave, high) ){ 1057*/ 1058 $$.e_retrans --; 1059 tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)lowsave, 1060 (caddr_t)high, $$.e_retrans, 1061 ($P.tp_Nretrans - $$.e_retrans) * (int)$P.tp_dt_ticks); 1062/* CE_BIT 1063 } 1064*/ 1065 } 1066; 1067 1068/* TP4 only */ 1069SAME <== TP_CLOSING TM_retrans 1070 ( $P.tp_retrans > 0 ) 1071 { 1072 $P.tp_retrans --; 1073 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL); 1074 IncStat(ts_retrans_dr); 1075 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 1076 } 1077; 1078 1079/* TP4 only */ 1080TP_REFWAIT <== TP_CLOSING TM_retrans 1081 DEFAULT /* no more retrans - gave up */ 1082 { 1083 $P.tp_sock->so_error = ETIMEDOUT; 1084 $P.tp_refp->tpr_state = REF_FROZEN; 1085 tp_recycle_tsuffix( $P ); 1086 tp_etimeout($P.tp_refp, TM_reference, 0,0,0, (int)$P.tp_refer_ticks); 1087 } 1088; 1089 1090/* 1091 * The resources are kept around until the ref timer goes off. 1092 * The suffices are wiped out sooner so they can be reused right away. 1093 */ 1094/* applicable in TP4, TP0 */ 1095TP_CLOSED <== TP_REFWAIT TM_reference 1096 DEFAULT 1097 { 1098 tp_freeref($P.tp_refp); 1099 tp_detach($P); 1100 } 1101; 1102 1103/* applicable in TP4, TP0 */ 1104/* A duplicate CR from connectionless network layer can't happen */ 1105SAME <== TP_OPEN [ CR_TPDU, CC_TPDU ] 1106 DEFAULT 1107 { 1108 if( $P.tp_class != TP_CLASS_0) { 1109 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1110 if ( $E.ev_number == CC_TPDU ) 1111 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1112 } 1113 /* ignore it if class 0 - state tables are blank for this */ 1114 } 1115; 1116 1117/* applicable in TP4, TP0 */ 1118SAME <== TP_OPEN T_DATA_req 1119 DEFAULT 1120 { 1121 IFTRACE(D_DATA) 1122 tptrace(TPPTmisc, "T_DATA_req sndhiwat snduna fcredit, tpcb", 1123 $P.tp_sndhiwat, $P.tp_snduna, $P.tp_fcredit, $P); 1124 ENDTRACE 1125 1126 tp_send($P); 1127 } 1128; 1129 1130/* TP4 only */ 1131SAME <== TP_OPEN T_XPD_req 1132 DEFAULT 1133 /* T_XPD_req was issued by sosend iff xpd socket buf was empty 1134 * at time of sosend(), 1135 * AND (which means) there were no unacknowledged XPD tpdus outstanding! 1136 */ 1137 { 1138 int error = 0; 1139 1140 /* resume XPD */ 1141 if ( $P.tp_Xsnd.sb_mb ) { 1142 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); 1143 /* m_copy doesn't preserve the m_xlink field, but at this pt. 1144 * that doesn't matter 1145 */ 1146 1147 IFTRACE(D_XPD) 1148 tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndhiwat snduna", 1149 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat, 1150 $P.tp_snduna); 1151 ENDTRACE 1152 IFDEBUG(D_XPD) 1153 printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc); 1154 dump_mbuf(m, "XPD req emitting M"); 1155 ENDDEBUG 1156 error = 1157 tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1158 $P.tp_retrans = $P.tp_Nretrans; 1159 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks); 1160 SEQ_INC($P, $P.tp_Xsndnxt); 1161 } 1162 if(trick_hc) 1163 return error; 1164 } 1165; 1166 1167/* TP4, faked ack in TP0 when cons send completes */ 1168SAME <== TP_OPEN AK_TPDU 1169 ( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq) ) 1170 1171 /* tp_goodack == true means 1172 * EITHER it actually acked something heretofore unacknowledged 1173 * OR no news but the credit should be processed. 1174 */ 1175 { 1176 IFDEBUG(D_ACKRECV) 1177 printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt); 1178 ENDDEBUG 1179 if( $P.tp_class != TP_CLASS_0) { 1180 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1181 tp_euntimeout_lss($P.tp_refp, TM_data_retrans, $$.e_seq); 1182 } 1183 sbwakeup( &$P.tp_sock->so_snd ); 1184 1185 if ($P.tp_sndhiwat <= $P.tp_retrans_hiwat && 1186 $P.tp_snduna <= $P.tp_retrans_hiwat) { 1187 1188 register struct mbuf *m; 1189 /* extern struct mbuf *m_copy(); */ 1190 register struct tp_rtc *r; 1191 SeqNum high, retrans, low_save; 1192 1193 high = SEQ_MIN($P, SEQ_ADD($P, $P.tp_snduna, 1194 MIN($P.tp_cong_win, $P.tp_fcredit)) - 1, 1195 $P.tp_sndhiwat); 1196 low_save = retrans = SEQ_MAX($P, SEQ_ADD($P, $P.tp_last_retrans, 1), 1197 $P.tp_snduna); 1198 for (; SEQ_LEQ($P, retrans, high); SEQ_INC($P, retrans)) { 1199 1200 for (r = $P.tp_snduna_rtc; r; r = r->tprt_next){ 1201 if ( r->tprt_seq == retrans ){ 1202 if(( m = m_copy(r->tprt_data, 0, r->tprt_octets )) 1203 == MNULL) 1204 break; 1205 (void) tp_emit(DT_TPDU_type, $P, retrans, 1206 r->tprt_eot, m); 1207 $P.tp_last_retrans = retrans; 1208 IncStat(ts_retrans_dt); 1209 break; 1210 } 1211 } 1212 if ( r == (struct tp_rtc *)0 ){ 1213 IFDEBUG(D_RTC) 1214 printf( "tp: retrans rtc list is GONE!\n"); 1215 ENDDEBUG 1216 break; 1217 } 1218 } 1219 tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)low_save, 1220 (caddr_t)high, $P.tp_retrans, (int)$P.tp_dt_ticks); 1221 if (SEQ_DEC($P, retrans) == $P.tp_retrans_hiwat) 1222 tp_send($P); 1223 } 1224 else { 1225 tp_send($P); 1226 } 1227 IFDEBUG(D_ACKRECV) 1228 printf("GOOD ACK new sndhiwat 0x%x\n", $P.tp_sndhiwat); 1229 ENDDEBUG 1230 } 1231; 1232 1233/* TP4, and TP0 after sending a CC or possibly a CR */ 1234SAME <== TP_OPEN AK_TPDU 1235 DEFAULT 1236 { 1237 IFTRACE(D_ACKRECV) 1238 tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq", 1239 $$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0); 1240 ENDTRACE 1241 if( $P.tp_class != TP_CLASS_0 ) { 1242 1243 if ( !$$.e_fcc_present ) { 1244 /* send ACK with FCC */ 1245 IncStat( ts_ackreason[_ACK_FCC_] ); 1246 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL); 1247 } 1248 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1249 } 1250 } 1251; 1252 1253/* NBS(47) */ 1254 /* goes in at *** */ 1255 /* just so happens that this is never true now, because we allow 1256 * only 1 packet in the queue at once (this could be changed) 1257 if ( $P.tp_Xsnd.sb_mb ) { 1258 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??); 1259 1260 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1261 $P.tp_retrans = $P.tp_Nretrans; 1262 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks); 1263 SEQ_INC($P, $P.tp_Xsndnxt); 1264 } 1265 */ 1266 /* end of the above hack */ 1267 1268/* TP4 only */ 1269SAME <== TP_OPEN XAK_TPDU 1270 ( tp_goodXack($P, $$.e_seq) ) 1271 /* tp_goodXack checks for good ack, removes the correct 1272 * tpdu from the queue and returns 1 if ack was legit, 0 if not. 1273 * also updates tp_Xuna 1274 */ 1275 { 1276 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1277 tp_cuntimeout($P.tp_refp, TM_retrans); 1278 1279 sbwakeup( &$P.tp_sock->so_snd ); 1280 1281 /* resume normal data */ 1282 tp_send($P); 1283 } 1284; 1285 1286/* TP4, and TP0 after sending a CC or possibly a CR */ 1287SAME <== TP_OPEN XAK_TPDU 1288 DEFAULT 1289 { 1290 IFTRACE(D_ACKRECV) 1291 tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0); 1292 ENDTRACE 1293 if( $P.tp_class != TP_CLASS_0 ) { 1294 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1295 } 1296 } 1297; 1298 1299/* TP4 only */ 1300SAME <== TP_OPEN TM_sendack 1301 DEFAULT 1302 { 1303 IFTRACE(D_TIMER) 1304 tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe, 1305 $P.tp_sent_lcdt, 0); 1306 ENDTRACE 1307 IncPStat($P, tps_n_TMsendack); 1308 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1309 } 1310; 1311 1312/* TP0 only */ 1313SAME <== TP_OPEN T_USR_rcvd 1314 ($P.tp_class == TP_CLASS_0) 1315 { 1316 if (sbspace(&$P.tp_sock->so_rcv) > 0) 1317 tp0_openflow($P); 1318 } 1319; 1320 1321/* TP4 only */ 1322 /* If old credit was zero, 1323 * we'd better inform other side that we now have space 1324 * But this is not enough. Sender might not yet have 1325 * seen an ack with cdt 0 but it might still think the 1326 * window is closed, so it's going to wait. 1327 * Best to send an ack each time. 1328 * Strictly speaking, this ought to be a function of the 1329 * general ack strategy. 1330 */ 1331SAME <== TP_OPEN T_USR_rcvd 1332 DEFAULT 1333 { 1334 if( trick_hc ) { 1335 1336 LOCAL_CREDIT($P); 1337 /* 1338 * If the window has is now closed or has only 1339 * one slot left and an ACK will increase this (local 1340 * credit is greater than or equal to 1) send an AK 1341 * here. Otherwise, we will wait for more data or 1342 * the ack timer to fire. 1343 */ 1344#define CREDIT_SMALL 1 1345 if (SEQ_GEQ($P, $P.tp_rcvnxt, $P.tp_sent_uwe) && 1346 $P.tp_lcredit > CREDIT_SMALL) { 1347 IncStat(ts_ackreason[_ACK_USRRCV_]); 1348 return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1349 } 1350 } 1351 } 1352; 1353 1354/* applicable in TP4, TP0 */ 1355SAME <== TP_REFWAIT [ T_USR_rcvd, T_USR_Xrcvd ] 1356 DEFAULT 1357 /* This happens if other end sent a DR when the user was waiting 1358 * on a receive. 1359 * Processing the DR includes putting us in REFWAIT state. 1360 */ 1361 { 1362 if(trick_hc) 1363 return ECONNABORTED; 1364 } 1365; 1366 1367/* TP0 only */ 1368TP_REFWAIT <== [ TP_OPEN, TP_CRSENT, TP_LISTENING ] T_NETRESET 1369 ( $P.tp_class != TP_CLASS_4 ) 1370 /* 0 or (4 and 0) */ 1371 /* in OPEN class will be 0 or 4 but not both */ 1372 /* in CRSENT or LISTENING it could be in negotiation, hence both */ 1373 /* Actually, this shouldn't ever happen in LISTENING */ 1374 { 1375 ASSERT( $P.tp_state != TP_LISTENING ); 1376 tp_indicate(T_DISCONNECT, $P, ECONNRESET); 1377 tp_soisdisconnected($P); 1378 } 1379; 1380 1381/* TP4: ignore resets */ 1382SAME <== [ TP_OPEN, TP_CRSENT, TP_AKWAIT, 1383 TP_CLOSING, TP_LISTENING ] T_NETRESET 1384 DEFAULT 1385 NULLACTION 1386; 1387 1388/* applicable in TP4, TP0 */ 1389SAME <== [ TP_CLOSED, TP_REFWAIT ] T_NETRESET 1390 DEFAULT 1391 NULLACTION 1392; 1393 1394/* C'EST TOUT */ 1395