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.11 (Berkeley) 08/28/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.11 (Berkeley) 08/28/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 mbuf *m, *n = $P.tp_sock->so_snd.sb_mb; 1015 register SeqNum high = $$.e_high; 1016 1017 low = $P.tp_snduna; 1018 lowsave = high = low; 1019 1020 tp_euntimeout_lss($P.tp_refp, TM_data_retrans, 1021 SEQ_ADD($P, $P.tp_sndhiwat, 1)); 1022 $P.tp_retrans_hiwat = $P.tp_sndhiwat; 1023 1024 if (($P.tp_rx_strat & TPRX_EACH) == 0) 1025 high = (high>low)?low:high; 1026 1027 if( $P.tp_rx_strat & TPRX_USE_CW ) { 1028 register int i; 1029 1030 $P.tp_cong_win = 1; 1031 $P.tp_ackrcvd = 0; 1032 i = SEQ_ADD($P, low, $P.tp_cong_win); 1033 1034 high = SEQ_MIN($P, high, $P.tp_sndhiwat); 1035 1036 } 1037 1038 while (SEQ_LEQ($P, low, high)) { 1039 if (n) { 1040 if ((m = m_copy(n, 0, M_COPYALL))== MNULL) 1041 break; 1042 (void) tp_emit(DT_TPDU_type, $P, 1043 low, (m->m_flags & M_EOR) != 0, m); 1044 IncStat(ts_retrans_dt); 1045 SEQ_INC($P, low ); 1046 } 1047 n = n->m_nextpkt; 1048 } 1049/* CE_BIT 1050 if ( SEQ_LEQ($P, lowsave, high) ){ 1051*/ 1052 $$.e_retrans --; 1053 tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)lowsave, 1054 (caddr_t)high, $$.e_retrans, 1055 ($P.tp_Nretrans - $$.e_retrans) * (int)$P.tp_dt_ticks); 1056/* CE_BIT 1057 } 1058*/ 1059 } 1060; 1061 1062/* TP4 only */ 1063SAME <== TP_CLOSING TM_retrans 1064 ( $P.tp_retrans > 0 ) 1065 { 1066 $P.tp_retrans --; 1067 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL); 1068 IncStat(ts_retrans_dr); 1069 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_dr_ticks); 1070 } 1071; 1072 1073/* TP4 only */ 1074TP_REFWAIT <== TP_CLOSING TM_retrans 1075 DEFAULT /* no more retrans - gave up */ 1076 { 1077 $P.tp_sock->so_error = ETIMEDOUT; 1078 $P.tp_refp->tpr_state = REF_FROZEN; 1079 tp_recycle_tsuffix( $P ); 1080 tp_etimeout($P.tp_refp, TM_reference, 0,0,0, (int)$P.tp_refer_ticks); 1081 } 1082; 1083 1084/* 1085 * The resources are kept around until the ref timer goes off. 1086 * The suffices are wiped out sooner so they can be reused right away. 1087 */ 1088/* applicable in TP4, TP0 */ 1089TP_CLOSED <== TP_REFWAIT TM_reference 1090 DEFAULT 1091 { 1092 tp_freeref($P.tp_refp); 1093 tp_detach($P); 1094 } 1095; 1096 1097/* applicable in TP4, TP0 */ 1098/* A duplicate CR from connectionless network layer can't happen */ 1099SAME <== TP_OPEN [ CR_TPDU, CC_TPDU ] 1100 DEFAULT 1101 { 1102 if( $P.tp_class != TP_CLASS_0) { 1103 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1104 if ( $E.ev_number == CC_TPDU ) 1105 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1106 } 1107 /* ignore it if class 0 - state tables are blank for this */ 1108 } 1109; 1110 1111/* applicable in TP4, TP0 */ 1112SAME <== TP_OPEN T_DATA_req 1113 DEFAULT 1114 { 1115 IFTRACE(D_DATA) 1116 tptrace(TPPTmisc, "T_DATA_req sndhiwat snduna fcredit, tpcb", 1117 $P.tp_sndhiwat, $P.tp_snduna, $P.tp_fcredit, $P); 1118 ENDTRACE 1119 1120 tp_send($P); 1121 } 1122; 1123 1124/* TP4 only */ 1125SAME <== TP_OPEN T_XPD_req 1126 DEFAULT 1127 /* T_XPD_req was issued by sosend iff xpd socket buf was empty 1128 * at time of sosend(), 1129 * AND (which means) there were no unacknowledged XPD tpdus outstanding! 1130 */ 1131 { 1132 int error = 0; 1133 1134 /* resume XPD */ 1135 if ( $P.tp_Xsnd.sb_mb ) { 1136 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); 1137 /* m_copy doesn't preserve the m_xlink field, but at this pt. 1138 * that doesn't matter 1139 */ 1140 1141 IFTRACE(D_XPD) 1142 tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndhiwat snduna", 1143 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndhiwat, 1144 $P.tp_snduna); 1145 ENDTRACE 1146 IFDEBUG(D_XPD) 1147 printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc); 1148 dump_mbuf(m, "XPD req emitting M"); 1149 ENDDEBUG 1150 error = 1151 tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1152 $P.tp_retrans = $P.tp_Nretrans; 1153 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks); 1154 SEQ_INC($P, $P.tp_Xsndnxt); 1155 } 1156 if(trick_hc) 1157 return error; 1158 } 1159; 1160 1161/* TP4, faked ack in TP0 when cons send completes */ 1162SAME <== TP_OPEN AK_TPDU 1163 ( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq) ) 1164 1165 /* tp_goodack == true means 1166 * EITHER it actually acked something heretofore unacknowledged 1167 * OR no news but the credit should be processed. 1168 */ 1169 { 1170 struct sockbuf *sb = &$P.tp_sock->so_snd; 1171 1172 IFDEBUG(D_ACKRECV) 1173 printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt); 1174 ENDDEBUG 1175 if( $P.tp_class != TP_CLASS_0) { 1176 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1177 tp_euntimeout_lss($P.tp_refp, TM_data_retrans, $$.e_seq); 1178 } 1179 sbwakeup(sb); 1180 if ($P.tp_sndhiwat <= $P.tp_retrans_hiwat && 1181 $P.tp_snduna <= $P.tp_retrans_hiwat) { 1182 1183 register struct mbuf *m, *n = sb->sb_mb; 1184 SeqNum high, retrans, lowsave; 1185 1186 high = SEQ_MIN($P, SEQ_ADD($P, $P.tp_snduna, 1187 MIN($P.tp_cong_win, $P.tp_fcredit)) - 1, 1188 $P.tp_sndhiwat); 1189 retrans = SEQ_MAX($P, SEQ_ADD($P, $P.tp_last_retrans, 1), 1190 $P.tp_snduna); 1191 for (lowsave = $P.tp_snduna; 1192 SEQ_LT($P, lowsave, retrans); SEQ_INC($P, lowsave)) 1193 n = n->m_nextpkt; 1194 for (; SEQ_LEQ($P, retrans, high); SEQ_INC($P, retrans)) { 1195 if (n == 0) { 1196 IFDEBUG(D_RTC) 1197 printf("tp: retrans list is GONE!\n"); 1198 ENDDEBUG 1199 break; 1200 } else if (m = m_copy(n, 0, M_COPYALL)) { 1201 (void) tp_emit(DT_TPDU_type, $P, retrans, 1202 (m->m_flags & M_EOR) != 0, m); 1203 $P.tp_last_retrans = retrans; 1204 IncStat(ts_retrans_dt); 1205 } 1206 n = n->m_nextpkt; 1207 } 1208 tp_etimeout($P.tp_refp, TM_data_retrans, (caddr_t)lowsave, 1209 (caddr_t)high, $P.tp_retrans, (int)$P.tp_dt_ticks); 1210 if (SEQ_DEC($P, retrans) == $P.tp_retrans_hiwat) 1211 tp_send($P); 1212 } 1213 else { 1214 tp_send($P); 1215 } 1216 IFDEBUG(D_ACKRECV) 1217 printf("GOOD ACK new sndhiwat 0x%x\n", $P.tp_sndhiwat); 1218 ENDDEBUG 1219 } 1220; 1221 1222/* TP4, and TP0 after sending a CC or possibly a CR */ 1223SAME <== TP_OPEN AK_TPDU 1224 DEFAULT 1225 { 1226 IFTRACE(D_ACKRECV) 1227 tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq", 1228 $$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0); 1229 ENDTRACE 1230 if( $P.tp_class != TP_CLASS_0 ) { 1231 1232 if ( !$$.e_fcc_present ) { 1233 /* send ACK with FCC */ 1234 IncStat( ts_ackreason[_ACK_FCC_] ); 1235 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL); 1236 } 1237 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1238 } 1239 } 1240; 1241 1242/* NBS(47) */ 1243 /* goes in at *** */ 1244 /* just so happens that this is never true now, because we allow 1245 * only 1 packet in the queue at once (this could be changed) 1246 if ( $P.tp_Xsnd.sb_mb ) { 1247 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??); 1248 1249 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1250 $P.tp_retrans = $P.tp_Nretrans; 1251 tp_ctimeout($P.tp_refp, TM_retrans, (int)$P.tp_xpd_ticks); 1252 SEQ_INC($P, $P.tp_Xsndnxt); 1253 } 1254 */ 1255 /* end of the above hack */ 1256 1257/* TP4 only */ 1258SAME <== TP_OPEN XAK_TPDU 1259 ( tp_goodXack($P, $$.e_seq) ) 1260 /* tp_goodXack checks for good ack, removes the correct 1261 * tpdu from the queue and returns 1 if ack was legit, 0 if not. 1262 * also updates tp_Xuna 1263 */ 1264 { 1265 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1266 tp_cuntimeout($P.tp_refp, TM_retrans); 1267 1268 sbwakeup( &$P.tp_sock->so_snd ); 1269 1270 /* resume normal data */ 1271 tp_send($P); 1272 } 1273; 1274 1275/* TP4, and TP0 after sending a CC or possibly a CR */ 1276SAME <== TP_OPEN XAK_TPDU 1277 DEFAULT 1278 { 1279 IFTRACE(D_ACKRECV) 1280 tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0); 1281 ENDTRACE 1282 if( $P.tp_class != TP_CLASS_0 ) { 1283 tp_ctimeout($P.tp_refp, TM_inact, (int)$P.tp_inact_ticks); 1284 } 1285 } 1286; 1287 1288/* TP4 only */ 1289SAME <== TP_OPEN TM_sendack 1290 DEFAULT 1291 { 1292 IFTRACE(D_TIMER) 1293 tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe, 1294 $P.tp_sent_lcdt, 0); 1295 ENDTRACE 1296 IncPStat($P, tps_n_TMsendack); 1297 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1298 } 1299; 1300 1301/* TP0 only */ 1302SAME <== TP_OPEN T_USR_rcvd 1303 ($P.tp_class == TP_CLASS_0) 1304 { 1305 if (sbspace(&$P.tp_sock->so_rcv) > 0) 1306 tp0_openflow($P); 1307 } 1308; 1309 1310/* TP4 only */ 1311 /* If old credit was zero, 1312 * we'd better inform other side that we now have space 1313 * But this is not enough. Sender might not yet have 1314 * seen an ack with cdt 0 but it might still think the 1315 * window is closed, so it's going to wait. 1316 * Best to send an ack each time. 1317 * Strictly speaking, this ought to be a function of the 1318 * general ack strategy. 1319 */ 1320SAME <== TP_OPEN T_USR_rcvd 1321 DEFAULT 1322 { 1323 if( trick_hc ) { 1324 1325 LOCAL_CREDIT($P); 1326 /* 1327 * If the window has is now closed or has only 1328 * one slot left and an ACK will increase this (local 1329 * credit is greater than or equal to 1) send an AK 1330 * here. Otherwise, we will wait for more data or 1331 * the ack timer to fire. 1332 */ 1333#define CREDIT_SMALL 1 1334 if (SEQ_GEQ($P, $P.tp_rcvnxt, $P.tp_sent_uwe) && 1335 $P.tp_lcredit > CREDIT_SMALL) { 1336 IncStat(ts_ackreason[_ACK_USRRCV_]); 1337 return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1338 } 1339 } 1340 } 1341; 1342 1343/* applicable in TP4, TP0 */ 1344SAME <== TP_REFWAIT [ T_USR_rcvd, T_USR_Xrcvd ] 1345 DEFAULT 1346 /* This happens if other end sent a DR when the user was waiting 1347 * on a receive. 1348 * Processing the DR includes putting us in REFWAIT state. 1349 */ 1350 { 1351 if(trick_hc) 1352 return ECONNABORTED; 1353 } 1354; 1355 1356/* TP0 only */ 1357TP_REFWAIT <== [ TP_OPEN, TP_CRSENT, TP_LISTENING ] T_NETRESET 1358 ( $P.tp_class != TP_CLASS_4 ) 1359 /* 0 or (4 and 0) */ 1360 /* in OPEN class will be 0 or 4 but not both */ 1361 /* in CRSENT or LISTENING it could be in negotiation, hence both */ 1362 /* Actually, this shouldn't ever happen in LISTENING */ 1363 { 1364 ASSERT( $P.tp_state != TP_LISTENING ); 1365 tp_indicate(T_DISCONNECT, $P, ECONNRESET); 1366 tp_soisdisconnected($P); 1367 } 1368; 1369 1370/* TP4: ignore resets */ 1371SAME <== [ TP_OPEN, TP_CRSENT, TP_AKWAIT, 1372 TP_CLOSING, TP_LISTENING ] T_NETRESET 1373 DEFAULT 1374 NULLACTION 1375; 1376 1377/* applicable in TP4, TP0 */ 1378SAME <== [ TP_CLOSED, TP_REFWAIT ] T_NETRESET 1379 DEFAULT 1380 NULLACTION 1381; 1382 1383/* C'EST TOUT */ 1384