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.14 (Berkeley) 09/26/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.14 (Berkeley) 09/26/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_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 closed-window 136 * Probes. 137 * It's set w/ keepalive-ticks every time an ack is sent. 138 * (this is done in (void) tp_emit() ). 139 * Whenever a DT arrives which doesn't require immediate acking, 140 * a separate fast-timeout flag is set ensuring 200ms response. 141 */ 142 TM_notused 143 144 /* 145 * E (typically expired) timers - these may be in any order. 146 * These cause procedures to be executed directly; may not 147 * cause an 'event' as we know them here. 148 */ 149 TM_reference { SeqNum e_low; SeqNum e_high; int e_retrans; } 150 TM_data_retrans { SeqNum e_low; SeqNum e_high; int e_retrans; } 151 152/* NOTE: in tp_input is a minor optimization that assumes that 153 * for all tpdu types that can take e_data and e_datalen, these 154 * fields fall in the same place in the event structure, that is, 155 * e_data is the first field and e_datalen is the 2nd field. 156 */ 157 158 ER_TPDU { 159 u_char e_reason; 160 } 161 CR_TPDU { struct mbuf *e_data; /* first field */ 162 int e_datalen; /* 2nd field */ 163 u_int e_cdt; 164 } 165 DR_TPDU { struct mbuf *e_data; /* first field */ 166 int e_datalen; /* 2nd field */ 167 u_short e_sref; 168 u_char e_reason; 169 } 170 DC_TPDU 171 CC_TPDU { struct mbuf *e_data; /* first field */ 172 int e_datalen; /* 2nd field */ 173 u_short e_sref; 174 u_int e_cdt; 175 } 176 AK_TPDU { u_int e_cdt; 177 SeqNum e_seq; 178 SeqNum e_subseq; 179 u_char e_fcc_present; 180 } 181 DT_TPDU { struct mbuf *e_data; /* first field */ 182 int e_datalen; /* 2nd field */ 183 u_int e_eot; 184 SeqNum e_seq; 185 } 186 XPD_TPDU { struct mbuf *e_data; /* first field */ 187 int e_datalen; /* 2nd field */ 188 SeqNum e_seq; 189 } 190 XAK_TPDU { SeqNum e_seq; } 191 192 T_CONN_req 193 T_DISC_req { u_char e_reason; } 194 T_LISTEN_req 195 T_DATA_req 196 T_XPD_req 197 T_USR_rcvd 198 T_USR_Xrcvd 199 T_DETACH 200 T_NETRESET 201 T_ACPT_req 202 203 204*TRANSITIONS 205 206 207/* TP_AKWAIT doesn't exist in TP 0 */ 208SAME <== TP_AKWAIT [ CC_TPDU, DC_TPDU, XAK_TPDU ] 209 DEFAULT 210 NULLACTION 211; 212 213 214/* applicable in TP4, TP0 */ 215SAME <== TP_REFWAIT DR_TPDU 216 ( $$.e_sref != 0 ) 217 { 218 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); 219 } 220; 221 222/* applicable in TP4, TP0 */ 223SAME <== TP_REFWAIT [ CR_TPDU, CC_TPDU, DT_TPDU, 224 DR_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU, DC_TPDU, ER_TPDU ] 225 DEFAULT 226 { 227# ifdef TP_DEBUG 228 if( $E.ev_number != AK_TPDU ) 229 printf("TPDU 0x%x in REFWAIT!!!!\n", $E.ev_number); 230# endif TP_DEBUG 231 } 232; 233 234/* applicable in TP4, TP0 */ 235SAME <== TP_REFWAIT [ T_DETACH, T_DISC_req ] 236 DEFAULT 237 NULLACTION 238; 239 240/* applicable in TP4, TP0 */ 241SAME <== TP_CRSENT AK_TPDU 242 ($P.tp_class == TP_CLASS_0) 243 { 244 /* oh, man is this grotesque or what? */ 245 (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq); 246 /* but it's necessary because this pseudo-ack may happen 247 * before the CC arrives, but we HAVE to adjust the 248 * snduna as a result of the ack, WHENEVER it arrives 249 */ 250 } 251; 252 253/* applicable in TP4, TP0 */ 254SAME <== TP_CRSENT 255 [ CR_TPDU, DC_TPDU, DT_TPDU, XPD_TPDU, XAK_TPDU ] 256 DEFAULT 257 NULLACTION 258; 259 260/* applicable in TP4, TP0 */ 261SAME <== TP_CLOSED [ DT_TPDU, XPD_TPDU, 262 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ] 263 DEFAULT 264 NULLACTION 265; 266 267/* TP_CLOSING doesn't exist in TP 0 */ 268SAME <== TP_CLOSING 269 [ CC_TPDU, CR_TPDU, DT_TPDU, XPD_TPDU, AK_TPDU, XAK_TPDU ] 270 DEFAULT 271 NULLACTION 272; 273 274 275/* DC_TPDU doesn't exist in TP 0 */ 276SAME <== TP_OPEN DC_TPDU 277 DEFAULT 278 NULLACTION 279; 280 281/* applicable in TP4, TP0 */ 282SAME <== TP_LISTENING [DR_TPDU, CC_TPDU, DT_TPDU, XPD_TPDU, 283 ER_TPDU, DC_TPDU, AK_TPDU, XAK_TPDU ] 284 DEFAULT 285 NULLACTION 286; 287 288/* applicable in TP4, TP0 */ 289TP_LISTENING <== TP_CLOSED T_LISTEN_req 290 DEFAULT 291 NULLACTION 292; 293 294/* applicable in TP4, TP0 */ 295TP_CLOSED <== [ TP_LISTENING, TP_CLOSED ] T_DETACH 296 DEFAULT 297 { 298 tp_detach($P); 299 } 300; 301 302TP_CONFIRMING <== TP_LISTENING CR_TPDU 303 ( $P.tp_class == TP_CLASS_0) 304 { 305 $P.tp_refstate = REF_OPEN; /* has timers ??? */ 306 } 307; 308 309TP_CONFIRMING <== TP_LISTENING CR_TPDU 310 DEFAULT 311 { 312 IFTRACE(D_CONN) 313 tptrace(TPPTmisc, "CR datalen data", $$.e_datalen, $$.e_data,0,0); 314 ENDTRACE 315 IFDEBUG(D_CONN) 316 printf("CR datalen 0x%x data 0x%x", $$.e_datalen, $$.e_data); 317 ENDDEBUG 318 $P.tp_refstate = REF_OPEN; /* has timers */ 319 $P.tp_fcredit = $$.e_cdt; 320 321 if ($$.e_datalen > 0) { 322 /* n/a for class 0 */ 323 ASSERT($P.tp_Xrcv.sb_cc == 0); 324 sbappendrecord(&$P.tp_Xrcv, $$.e_data); 325 $$.e_data = MNULL; 326 } 327 } 328; 329 330TP_OPEN <== TP_CONFIRMING T_ACPT_req 331 ( $P.tp_class == TP_CLASS_0 ) 332 { 333 IncStat(ts_tp0_conn); 334 IFTRACE(D_CONN) 335 tptrace(TPPTmisc, "Confiming", $P, 0,0,0); 336 ENDTRACE 337 IFDEBUG(D_CONN) 338 printf("Confirming connection: $P" ); 339 ENDDEBUG 340 soisconnected($P.tp_sock); 341 (void) tp_emit(CC_TPDU_type, $P, 0,0, MNULL) ; 342 $P.tp_fcredit = 1; 343 } 344; 345 346TP_AKWAIT <== TP_CONFIRMING T_ACPT_req 347 (tp_emit(CC_TPDU_type, $P, 0,0, MCPY($P.tp_ucddata, M_NOWAIT)) == 0) 348 { 349 IncStat(ts_tp4_conn); /* even though not quite open */ 350 IFTRACE(D_CONN) 351 tptrace(TPPTmisc, "Confiming", $P, 0,0,0); 352 ENDTRACE 353 IFDEBUG(D_CONN) 354 printf("Confirming connection: $P" ); 355 ENDDEBUG 356 tp_getoptions($P); 357 soisconnecting($P.tp_sock); 358 if (($P.tp_rx_strat & TPRX_FASTSTART) && ($P.tp_fcredit > 0)) 359 $P.tp_cong_win = $P.tp_fcredit * $P.tp_l_tpdusize; 360 $P.tp_retrans = $P.tp_Nretrans; 361 tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks); 362 } 363; 364 365/* TP4 only */ 366TP_CLOSED <== TP_CONFIRMING T_ACPT_req 367 DEFAULT /* emit failed */ 368 { 369 register struct tp_ref *r = $P.tp_refp; 370 371 IFDEBUG(D_CONN) 372 printf("event: CR_TPDU emit CC failed done " ); 373 ENDDEBUG 374 soisdisconnected($P.tp_sock); 375 tp_recycle_tsuffix( $P ); 376 tp_freeref(r); 377 tp_detach($P); 378 } 379; 380 381/* applicable in TP4, TP0 */ 382TP_CRSENT <== TP_CLOSED T_CONN_req 383 DEFAULT 384 { 385 int error; 386 struct mbuf *data = MNULL; 387 388 IFTRACE(D_CONN) 389 tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int)$P.tp_flags, 390 $P.tp_ucddata, 0, 0); 391 ENDTRACE 392 data = MCPY($P.tp_ucddata, M_WAIT); 393 if (data) { 394 IFDEBUG(D_CONN) 395 printf("T_CONN_req.trans m_copy cc 0x%x\n", 396 $P.tp_ucddata); 397 dump_mbuf(data, "sosnd @ T_CONN_req"); 398 ENDDEBUG 399 } 400 401 if (error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) 402 return error; /* driver WON'T change state; will return error */ 403 404 $P.tp_refstate = REF_OPEN; /* has timers */ 405 if($P.tp_class != TP_CLASS_0) { 406 $P.tp_retrans = $P.tp_Nretrans; 407 tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks); 408 } 409 } 410; 411 412/* applicable in TP4, TP0, but state TP_AKWAIT doesn't apply to TP0 */ 413TP_REFWAIT <== [ TP_CRSENT, TP_AKWAIT, TP_OPEN ] DR_TPDU 414 DEFAULT 415 { 416 sbflush(&$P.tp_Xrcv); /* purge non-delivered data data */ 417 if ($$.e_datalen > 0) { 418 sbappendrecord(&$P.tp_Xrcv, $$.e_data); 419 $$.e_data = MNULL; 420 } 421 tp_indicate(T_DISCONNECT, $P, 0); 422 tp_soisdisconnected($P); 423 if ($P.tp_class != TP_CLASS_0) { 424 if ($P.tp_state == TP_OPEN ) { 425 tp_euntimeout($P, TM_data_retrans); /* all */ 426 tp_cuntimeout($P, TM_retrans); 427 tp_cuntimeout($P, TM_inact); 428 tp_cuntimeout($P, TM_sendack); 429 } 430 tp_cuntimeout($P, TM_retrans); 431 if( $$.e_sref != 0 ) 432 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); 433 } 434 } 435; 436 437SAME <== TP_CLOSED DR_TPDU 438 DEFAULT 439 { 440 if( $$.e_sref != 0 ) 441 (void) tp_emit(DC_TPDU_type, $P, 0, 0, MNULL); 442 /* reference timer already set - reset it to be safe (???) */ 443 tp_euntimeout($P, TM_reference); /* all */ 444 tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks); 445 } 446; 447 448/* NBS(34) */ 449TP_REFWAIT <== TP_CRSENT ER_TPDU 450 DEFAULT 451 { 452 tp_cuntimeout($P, TM_retrans); 453 tp_indicate(ER_TPDU, $P, $$.e_reason); 454 tp_soisdisconnected($P); 455 } 456; 457 458/* NBS(27) */ 459TP_REFWAIT <== TP_CLOSING DR_TPDU 460 DEFAULT 461 { 462 tp_cuntimeout($P, TM_retrans); 463 tp_soisdisconnected($P); 464 } 465; 466/* these two transitions are the same but can't be combined because xebec 467 * can't handle the use of $$.e_reason if they're combined 468 */ 469/* NBS(27) */ 470TP_REFWAIT <== TP_CLOSING ER_TPDU 471 DEFAULT 472 { 473 tp_indicate(ER_TPDU, $P, $$.e_reason); 474 tp_cuntimeout($P, TM_retrans); 475 tp_soisdisconnected($P); 476 } 477; 478/* NBS(27) */ 479TP_REFWAIT <== TP_CLOSING DC_TPDU 480 DEFAULT 481 { 482 tp_cuntimeout($P, TM_retrans); 483 tp_soisdisconnected($P); 484 } 485; 486 487/* NBS(21) */ 488SAME <== TP_CLOSED [ CC_TPDU, CR_TPDU ] 489 DEFAULT 490 { /* don't ask me why we have to do this - spec says so */ 491 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NO_SESSION, MNULL); 492 /* don't bother with retransmissions of the DR */ 493 } 494; 495 496/* NBS(34) */ 497TP_REFWAIT <== TP_OPEN ER_TPDU 498 ($P.tp_class == TP_CLASS_0) 499 { 500 tp_soisdisconnecting($P.tp_sock); 501 tp_indicate(ER_TPDU, $P, $$.e_reason); 502 tp_soisdisconnected($P); 503 tp_netcmd( $P, CONN_CLOSE ); 504 } 505; 506 507TP_CLOSING <== [ TP_AKWAIT, TP_OPEN ] ER_TPDU 508 DEFAULT 509 { 510 if ($P.tp_state == TP_OPEN) { 511 tp_euntimeout($P, TM_data_retrans); /* all */ 512 tp_cuntimeout($P, TM_inact); 513 tp_cuntimeout($P, TM_sendack); 514 } 515 tp_soisdisconnecting($P.tp_sock); 516 tp_indicate(ER_TPDU, $P, $$.e_reason); 517 $P.tp_retrans = $P.tp_Nretrans; 518 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 519 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_PROTO_ERR, MNULL); 520 } 521; 522/* NBS(6) */ 523TP_OPEN <== TP_CRSENT CC_TPDU 524 ($P.tp_class == TP_CLASS_0) 525 { 526 tp_cuntimeout($P, TM_retrans); 527 IncStat(ts_tp0_conn); 528 $P.tp_fcredit = 1; 529 soisconnected($P.tp_sock); 530 } 531; 532 533TP_OPEN <== TP_CRSENT CC_TPDU 534 DEFAULT 535 { 536 IFDEBUG(D_CONN) 537 printf("trans: CC_TPDU in CRSENT state flags 0x%x\n", 538 (int)$P.tp_flags); 539 ENDDEBUG 540 IncStat(ts_tp4_conn); 541 $P.tp_fref = $$.e_sref; 542 $P.tp_fcredit = $$.e_cdt; 543 if (($P.tp_rx_strat & TPRX_FASTSTART) && ($$.e_cdt > 0)) 544 $P.tp_cong_win = $$.e_cdt * $P.tp_l_tpdusize; 545 tp_getoptions($P); 546 tp_cuntimeout($P, TM_retrans); 547 if ($P.tp_ucddata) { 548 IFDEBUG(D_CONN) 549 printf("dropping user connect data cc 0x%x\n", 550 $P.tp_ucddata->m_len); 551 ENDDEBUG 552 m_freem($P.tp_ucddata); 553 $P.tp_ucddata = 0; 554 } 555 soisconnected($P.tp_sock); 556 if ($$.e_datalen > 0) { 557 ASSERT($P.tp_Xrcv.sb_cc == 0); /* should be empty */ 558 sbappendrecord(&$P.tp_Xrcv, $$.e_data); 559 $$.e_data = MNULL; 560 } 561 562 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 563 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 564 } 565; 566 567/* TP4 only */ 568SAME <== TP_CRSENT TM_retrans 569 ( $P.tp_retrans > 0 ) 570 { 571 struct mbuf *data = MNULL; 572 int error; 573 574 IncStat(ts_retrans_cr); 575 $P.tp_cong_win = 1 * $P.tp_l_tpdusize; 576 data = MCPY($P.tp_ucddata, M_NOWAIT); 577 if($P.tp_ucddata) { 578 IFDEBUG(D_CONN) 579 printf("TM_retrans.trans m_copy cc 0x%x\n", data); 580 dump_mbuf($P.tp_ucddata, "sosnd @ TM_retrans"); 581 ENDDEBUG 582 if( data == MNULL ) 583 return ENOBUFS; 584 } 585 586 $P.tp_retrans --; 587 if( error = tp_emit(CR_TPDU_type, $P, 0, 0, data) ) { 588 $P.tp_sock->so_error = error; 589 } 590 tp_ctimeout($P, TM_retrans, (int)$P.tp_cr_ticks); 591 } 592; 593 594/* TP4 only */ 595TP_REFWAIT <== TP_CRSENT TM_retrans 596 DEFAULT /* no more CR retransmissions */ 597 { 598 IncStat(ts_conn_gaveup); 599 $P.tp_sock->so_error = ETIMEDOUT; 600 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 601 tp_soisdisconnected($P); 602 } 603; 604 605/* TP4 only */ 606SAME <== TP_AKWAIT CR_TPDU 607 DEFAULT 608 /* duplicate CR (which doesn't really exist in the context of 609 * a connectionless network layer) 610 * Doesn't occur in class 0. 611 */ 612 { 613 int error; 614 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 615 616 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) { 617 $P.tp_sock->so_error = error; 618 } 619 $P.tp_retrans = $P.tp_Nretrans; 620 tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks); 621 } 622; 623 624/* TP4 only */ 625TP_OPEN <== TP_AKWAIT DT_TPDU 626 ( IN_RWINDOW( $P, $$.e_seq, 627 $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) ) 628 { 629 int doack; 630 631 /* 632 * Get rid of any confirm or connect data, so that if we 633 * crash or close, it isn't thought of as disconnect data. 634 */ 635 if ($P.tp_ucddata) { 636 m_freem($P.tp_ucddata); 637 $P.tp_ucddata = 0; 638 } 639 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 640 tp_cuntimeout($P, TM_retrans); 641 soisconnected($P.tp_sock); 642 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 643 644 /* see also next 2 transitions, if you make any changes */ 645 646 doack = tp_stash($P, $E); 647 IFDEBUG(D_DATA) 648 printf("tp_stash returns %d\n",doack); 649 ENDDEBUG 650 651 if (doack) { 652 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); 653 tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks); 654 } else 655 tp_ctimeout( $P, TM_sendack, (int)$P.tp_sendack_ticks); 656 657 IFDEBUG(D_DATA) 658 printf("after stash calling sbwakeup\n"); 659 ENDDEBUG 660 } 661; 662 663SAME <== TP_OPEN DT_TPDU 664 ( $P.tp_class == TP_CLASS_0 ) 665 { 666 tp0_stash($P, $E); 667 sbwakeup( &$P.tp_sock->so_rcv ); 668 669 IFDEBUG(D_DATA) 670 printf("after stash calling sbwakeup\n"); 671 ENDDEBUG 672 } 673; 674 675/* TP4 only */ 676SAME <== TP_OPEN DT_TPDU 677 ( IN_RWINDOW( $P, $$.e_seq, 678 $P.tp_rcvnxt, SEQ($P, $P.tp_rcvnxt + $P.tp_lcredit)) ) 679 { 680 int doack; /* tells if we must ack immediately */ 681 682 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 683 sbwakeup( &$P.tp_sock->so_rcv ); 684 685 doack = tp_stash($P, $E); 686 IFDEBUG(D_DATA) 687 printf("tp_stash returns %d\n",doack); 688 ENDDEBUG 689 690 if(doack) 691 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); 692 else 693 tp_ctimeout_MIN( $P, TM_sendack, (int)$P.tp_sendack_ticks); 694 695 IFDEBUG(D_DATA) 696 printf("after stash calling sbwakeup\n"); 697 ENDDEBUG 698 } 699; 700 701/* Not in window - we must ack under certain circumstances, namely 702 * a) if the seq number is below lwe but > lwe - (max credit ever given) 703 * (to handle lost acks) Can use max-possible-credit for this ^^^. 704 * and 705 * b) seq number is > uwe but < uwe + previously sent & withdrawn credit 706 * 707 * (see 12.2.3.8.1 of ISO spec, p. 73) 708 * We just always ack. 709 */ 710/* TP4 only */ 711SAME <== [ TP_OPEN, TP_AKWAIT ] DT_TPDU 712 DEFAULT /* Not in window */ 713 { 714 IFTRACE(D_DATA) 715 tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ", 716 $$.e_seq, $P.tp_rcvnxt, $P.tp_lcredit, 0); 717 ENDTRACE 718 IncStat(ts_dt_niw); 719 m_freem($$.e_data); 720 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 721 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL ); 722 } 723; 724 725/* TP4 only */ 726TP_OPEN <== TP_AKWAIT AK_TPDU 727 DEFAULT 728 { 729 if ($P.tp_ucddata) { 730 m_freem($P.tp_ucddata); 731 $P.tp_ucddata = 0; 732 } 733 (void) tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq); 734 tp_cuntimeout($P, TM_retrans); 735 736 soisconnected($P.tp_sock); 737 IFTRACE(D_CONN) 738 struct socket *so = $P.tp_sock; 739 tptrace(TPPTmisc, 740 "called sosiconn: so so_state rcv.sb_sel rcv.sb_flags", 741 so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags); 742 tptrace(TPPTmisc, 743 "called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head", 744 so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head); 745 ENDTRACE 746 747 tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks); 748 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 749 } 750; 751 752/* TP4 only */ 753TP_OPEN <== [ TP_OPEN, TP_AKWAIT ] XPD_TPDU 754 ($P.tp_Xrcvnxt == $$.e_seq) 755 { 756 if( $P.tp_state == TP_AKWAIT ) { 757 if ($P.tp_ucddata) { 758 m_freem($P.tp_ucddata); 759 $P.tp_ucddata = 0; 760 } 761 tp_cuntimeout($P, TM_retrans); 762 soisconnected($P.tp_sock); 763 tp_ctimeout($P, TM_sendack, (int)$P.tp_keepalive_ticks); 764 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 765 } 766 IFTRACE(D_XPD) 767 tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, e_seq datalen m_len\n", 768 $P.tp_Xrcvnxt,$$.e_seq, $$.e_datalen, $$.e_data->m_len); 769 ENDTRACE 770 771 $P.tp_sock->so_state |= SS_RCVATMARK; 772 $$.e_data->m_flags |= M_EOR; 773 sbinsertoob(&$P.tp_Xrcv, $$.e_data); 774 IFDEBUG(D_XPD) 775 dump_mbuf($$.e_data, "XPD TPDU: tp_Xrcv"); 776 ENDDEBUG 777 tp_indicate(T_XDATA, $P, 0); 778 sbwakeup( &$P.tp_Xrcv ); 779 780 (void) tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL); 781 SEQ_INC($P, $P.tp_Xrcvnxt); 782 } 783; 784 785/* TP4 only */ 786SAME <== TP_OPEN T_USR_Xrcvd 787 DEFAULT 788 { 789 if( $P.tp_Xrcv.sb_cc == 0 ) { 790 /* kludge for select(): */ 791 /* $P.tp_sock->so_state &= ~SS_OOBAVAIL; */ 792 } 793 } 794 /* OLD WAY: 795 * Ack only after the user receives the XPD. This is better for 796 * users that use one XPD right after another. 797 * Acking right away (the NEW WAY, see the prev. transition) is 798 * better for occasional * XPD, when the receiving user doesn't 799 * want to read the XPD immediately (which is session's behavior). 800 * 801 int error = tp_emit(XAK_TPDU_type, $P, $P.tp_Xrcvnxt, 0, MNULL); 802 SEQ_INC($P, $P.tp_Xrcvnxt); 803 return error; 804 */ 805; 806 807/* NOTE: presently if the user doesn't read the connection data 808 * before and expedited data PDU comes in, the connection data will 809 * be dropped. This is a bug. To avoid it, we need somewhere else 810 * to put the connection data. 811 * On the other hand, we need not to have it sitting around forever. 812 * This is a problem with the idea of trying to accommodate 813 * data on connect w/ a passive-open user interface. 814 */ 815/* TP4 only */ 816 817SAME <== [ TP_AKWAIT, TP_OPEN ] XPD_TPDU 818 DEFAULT /* not in window or cdt==0 */ 819 { 820 IFTRACE(D_XPD) 821 tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n", 822 $P.tp_Xrcvnxt, $$.e_seq, $P.tp_Xrcv.sb_cc , 0); 823 ENDTRACE 824 if( $P.tp_Xrcvnxt != $$.e_seq ) 825 IncStat(ts_xpd_niw); 826 if( $P.tp_Xrcv.sb_cc ) { 827 /* might as well kick 'em again */ 828 tp_indicate(T_XDATA, $P, 0); 829 IncStat(ts_xpd_dup); 830 } 831 m_freem($$.e_data); 832 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 833 /* don't send an xack because the xak gives "last one received", not 834 * "next one i expect" (dumb) 835 */ 836 } 837; 838 839/* Occurs (AKWAIT, OPEN) when parent (listening) socket gets aborted, and tries 840 * to detach all its "children" 841 * Also (CRSENT) when user kills a job that's doing a connect() 842 */ 843TP_REFWAIT <== TP_CRSENT T_DETACH 844 ($P.tp_class == TP_CLASS_0) 845 { 846 struct socket *so = $P.tp_sock; 847 848 /* detach from parent socket so it can finish closing */ 849 if (so->so_head) { 850 if (!soqremque(so, 0) && !soqremque(so, 1)) 851 panic("tp: T_DETACH"); 852 so->so_head = 0; 853 } 854 tp_soisdisconnecting($P.tp_sock); 855 tp_netcmd( $P, CONN_CLOSE); 856 tp_soisdisconnected($P); 857 } 858; 859 860/* TP4 only */ 861TP_CLOSING <== [ TP_CLOSING, TP_AKWAIT, TP_CRSENT, TP_CONFIRMING ] T_DETACH 862 DEFAULT 863 { 864 struct socket *so = $P.tp_sock; 865 struct mbuf *data = MNULL; 866 867 /* detach from parent socket so it can finish closing */ 868 if (so->so_head) { 869 if (!soqremque(so, 0) && !soqremque(so, 1)) 870 panic("tp: T_DETACH"); 871 so->so_head = 0; 872 } 873 if ($P.tp_state != TP_CLOSING) { 874 tp_soisdisconnecting($P.tp_sock); 875 data = MCPY($P.tp_ucddata, M_NOWAIT); 876 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_NORMAL_DISC, data); 877 $P.tp_retrans = $P.tp_Nretrans; 878 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 879 } 880 } 881; 882 883TP_REFWAIT <== [ TP_OPEN, TP_CRSENT ] T_DISC_req 884 ( $P.tp_class == TP_CLASS_0 ) 885 { 886 tp_soisdisconnecting($P.tp_sock); 887 tp_netcmd( $P, CONN_CLOSE); 888 tp_soisdisconnected($P); 889 } 890; 891 892/* TP4 only */ 893TP_CLOSING <== [ TP_AKWAIT, TP_OPEN, TP_CRSENT, TP_CONFIRMING ] T_DISC_req 894 DEFAULT 895 { 896 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 897 898 if($P.tp_state == TP_OPEN) { 899 tp_euntimeout($P, TM_data_retrans); /* all */ 900 tp_cuntimeout($P, TM_inact); 901 tp_cuntimeout($P, TM_sendack); 902 } 903 if (data) { 904 IFDEBUG(D_CONN) 905 printf("T_DISC_req.trans tp_ucddata 0x%x\n", 906 $P.tp_ucddata); 907 dump_mbuf(data, "ucddata @ T_DISC_req"); 908 ENDDEBUG 909 } 910 tp_soisdisconnecting($P.tp_sock); 911 $P.tp_retrans = $P.tp_Nretrans; 912 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 913 914 if( trick_hc ) 915 return tp_emit(DR_TPDU_type, $P, 0, $$.e_reason, data); 916 } 917; 918 919/* TP4 only */ 920SAME <== TP_AKWAIT TM_retrans 921 ( $P.tp_retrans > 0 ) 922 { 923 int error; 924 struct mbuf *data = MCPY($P.tp_ucddata, M_WAIT); 925 926 IncStat(ts_retrans_cc); 927 $P.tp_retrans --; 928 $P.tp_cong_win = 1 * $P.tp_l_tpdusize; 929 930 if( error = tp_emit(CC_TPDU_type, $P, 0, 0, data) ) 931 $P.tp_sock->so_error = error; 932 tp_ctimeout($P, TM_retrans, (int)$P.tp_cc_ticks); 933 } 934; 935 936/* TP4 only */ 937TP_CLOSING <== TP_AKWAIT TM_retrans 938 DEFAULT /* out of time */ 939 { 940 IncStat(ts_conn_gaveup); 941 tp_soisdisconnecting($P.tp_sock); 942 $P.tp_sock->so_error = ETIMEDOUT; 943 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 944 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST, MNULL); 945 $P.tp_retrans = $P.tp_Nretrans; 946 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 947 } 948; 949 950/* the retrans timers had better go off BEFORE the inactivity timer does, 951 * if transmissions are going on. 952 * (i.e., TM_inact should be greater than timer for all retrans plus ack 953 * turnaround) 954 */ 955/* TP4 only */ 956TP_CLOSING <== TP_OPEN [ TM_inact, TM_retrans, TM_data_retrans ] 957 DEFAULT 958 { 959 tp_euntimeout($P, TM_data_retrans); /* all */ 960 tp_cuntimeout($P, TM_inact); 961 tp_cuntimeout($P, TM_sendack); 962 963 IncStat(ts_conn_gaveup); 964 tp_soisdisconnecting($P.tp_sock); 965 $P.tp_sock->so_error = ETIMEDOUT; 966 tp_indicate(T_DISCONNECT, $P, ETIMEDOUT); 967 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_CONGEST_2, MNULL); 968 $P.tp_retrans = $P.tp_Nretrans; 969 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 970 } 971; 972 973/* TP4 only */ 974SAME <== TP_OPEN TM_retrans 975 ( $P.tp_retrans > 0 ) 976 { 977 $P.tp_cong_win = 1 * $P.tp_l_tpdusize; 978 /* resume XPD */ 979 if ( $P.tp_Xsnd.sb_mb ) { 980 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); 981 int shift; 982 983 IFTRACE(D_XPD) 984 tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna", 985 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt, 986 $P.tp_snduna); 987 ENDTRACE 988 IFDEBUG(D_XPD) 989 dump_mbuf(m, "XPD retrans emitting M"); 990 ENDDEBUG 991 IncStat(ts_retrans_xpd); 992 $P.tp_retrans --; 993 shift = max($P.tp_Nretrans - $P.tp_retrans, 6); 994 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 995 tp_ctimeout($P, TM_retrans, ((int)$P.tp_dt_ticks) << shift); 996 } 997 } 998; 999 1000/* TP4 only */ 1001SAME <== TP_OPEN TM_data_retrans 1002 ($P.tp_rxtshift < TP_NRETRANS) 1003 { 1004 $P.tp_rxtshift++; 1005 (void) tp_data_retrans($P); 1006 } 1007; 1008 1009/* TP4 only */ 1010SAME <== TP_CLOSING TM_retrans 1011 ( $P.tp_retrans > 0 ) 1012 { 1013 $P.tp_retrans --; 1014 (void) tp_emit(DR_TPDU_type, $P, 0, E_TP_DR_NO_REAS, MNULL); 1015 IncStat(ts_retrans_dr); 1016 tp_ctimeout($P, TM_retrans, (int)$P.tp_dr_ticks); 1017 } 1018; 1019 1020/* TP4 only */ 1021TP_REFWAIT <== TP_CLOSING TM_retrans 1022 DEFAULT /* no more retrans - gave up */ 1023 { 1024 $P.tp_sock->so_error = ETIMEDOUT; 1025 $P.tp_refstate = REF_FROZEN; 1026 tp_recycle_tsuffix( $P ); 1027 tp_etimeout($P, TM_reference, (int)$P.tp_refer_ticks); 1028 } 1029; 1030 1031/* 1032 * The resources are kept around until the ref timer goes off. 1033 * The suffices are wiped out sooner so they can be reused right away. 1034 */ 1035/* applicable in TP4, TP0 */ 1036TP_CLOSED <== TP_REFWAIT TM_reference 1037 DEFAULT 1038 { 1039 tp_freeref($P.tp_refp); 1040 tp_detach($P); 1041 } 1042; 1043 1044/* applicable in TP4, TP0 */ 1045/* A duplicate CR from connectionless network layer can't happen */ 1046SAME <== TP_OPEN [ CR_TPDU, CC_TPDU ] 1047 DEFAULT 1048 { 1049 if( $P.tp_class != TP_CLASS_0) { 1050 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 1051 if ( $E.ev_number == CC_TPDU ) 1052 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1053 } 1054 /* ignore it if class 0 - state tables are blank for this */ 1055 } 1056; 1057 1058/* applicable in TP4, TP0 */ 1059SAME <== TP_OPEN T_DATA_req 1060 DEFAULT 1061 { 1062 IFTRACE(D_DATA) 1063 tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb", 1064 $P.tp_sndnxt, $P.tp_snduna, $P.tp_fcredit, $P); 1065 ENDTRACE 1066 1067 tp_send($P); 1068 } 1069; 1070 1071/* TP4 only */ 1072SAME <== TP_OPEN T_XPD_req 1073 DEFAULT 1074 /* T_XPD_req was issued by sosend iff xpd socket buf was empty 1075 * at time of sosend(), 1076 * AND (which means) there were no unacknowledged XPD tpdus outstanding! 1077 */ 1078 { 1079 int error = 0; 1080 1081 /* resume XPD */ 1082 if ( $P.tp_Xsnd.sb_mb ) { 1083 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, (int)$P.tp_Xsnd.sb_cc); 1084 /* m_copy doesn't preserve the m_xlink field, but at this pt. 1085 * that doesn't matter 1086 */ 1087 1088 IFTRACE(D_XPD) 1089 tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna", 1090 $P.tp_Xuna, $P.tp_Xsndnxt, $P.tp_sndnxt, 1091 $P.tp_snduna); 1092 ENDTRACE 1093 IFDEBUG(D_XPD) 1094 printf("T_XPD_req: sb_cc 0x%x\n", $P.tp_Xsnd.sb_cc); 1095 dump_mbuf(m, "XPD req emitting M"); 1096 ENDDEBUG 1097 error = 1098 tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1099 $P.tp_retrans = $P.tp_Nretrans; 1100 1101 tp_ctimeout($P, TM_retrans, (int)$P.tp_rxtcur); 1102 SEQ_INC($P, $P.tp_Xsndnxt); 1103 } 1104 if(trick_hc) 1105 return error; 1106 } 1107; 1108 1109/* TP4, faked ack in TP0 when cons send completes */ 1110SAME <== TP_OPEN AK_TPDU 1111 ( tp_goodack($P, $$.e_cdt, $$.e_seq, $$.e_subseq) ) 1112 1113 /* tp_goodack == true means 1114 * EITHER it actually acked something heretofore unacknowledged 1115 * OR no news but the credit should be processed. 1116 */ 1117 { 1118 struct sockbuf *sb = &$P.tp_sock->so_snd; 1119 1120 IFDEBUG(D_ACKRECV) 1121 printf("GOOD ACK seq 0x%x cdt 0x%x\n", $$.e_seq, $$.e_cdt); 1122 ENDDEBUG 1123 if( $P.tp_class != TP_CLASS_0) { 1124 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 1125 } 1126 sbwakeup(sb); 1127 IFDEBUG(D_ACKRECV) 1128 printf("GOOD ACK new sndnxt 0x%x\n", $P.tp_sndnxt); 1129 ENDDEBUG 1130 } 1131; 1132 1133/* TP4, and TP0 after sending a CC or possibly a CR */ 1134SAME <== TP_OPEN AK_TPDU 1135 DEFAULT 1136 { 1137 IFTRACE(D_ACKRECV) 1138 tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq", 1139 $$.e_fcc_present, $P.tp_r_subseq, $$.e_subseq, 0); 1140 ENDTRACE 1141 if( $P.tp_class != TP_CLASS_0 ) { 1142 1143 if ( !$$.e_fcc_present ) { 1144 /* send ACK with FCC */ 1145 IncStat( ts_ackreason[_ACK_FCC_] ); 1146 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 1, MNULL); 1147 } 1148 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 1149 } 1150 } 1151; 1152 1153/* NBS(47) */ 1154 /* goes in at *** */ 1155 /* just so happens that this is never true now, because we allow 1156 * only 1 packet in the queue at once (this could be changed) 1157 if ( $P.tp_Xsnd.sb_mb ) { 1158 struct mbuf *m = m_copy($P.tp_Xsnd.sb_mb, 0, ??); 1159 1160 (void) tp_emit(XPD_TPDU_type, $P, $P.tp_Xuna, 1, m); 1161 $P.tp_retrans = $P.tp_Nretrans; 1162 tp_ctimeout($P, TM_retrans, (int)$P.tp_xpd_ticks); 1163 SEQ_INC($P, $P.tp_Xsndnxt); 1164 } 1165 */ 1166 /* end of the above hack */ 1167 1168/* TP4 only */ 1169SAME <== TP_OPEN XAK_TPDU 1170 ( tp_goodXack($P, $$.e_seq) ) 1171 /* tp_goodXack checks for good ack, removes the correct 1172 * tpdu from the queue and returns 1 if ack was legit, 0 if not. 1173 * also updates tp_Xuna 1174 */ 1175 { 1176 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 1177 tp_cuntimeout($P, TM_retrans); 1178 1179 sbwakeup( &$P.tp_sock->so_snd ); 1180 1181 /* resume normal data */ 1182 tp_send($P); 1183 } 1184; 1185 1186/* TP4, and TP0 after sending a CC or possibly a CR */ 1187SAME <== TP_OPEN XAK_TPDU 1188 DEFAULT 1189 { 1190 IFTRACE(D_ACKRECV) 1191 tptrace(TPPTmisc, "BOGUS XACK eventtype ", $E.ev_number, 0, 0,0); 1192 ENDTRACE 1193 if( $P.tp_class != TP_CLASS_0 ) { 1194 tp_ctimeout($P, TM_inact, (int)$P.tp_inact_ticks); 1195 } 1196 } 1197; 1198 1199/* TP4 only */ 1200SAME <== TP_OPEN TM_sendack 1201 DEFAULT 1202 { 1203 int timo; 1204 IFTRACE(D_TIMER) 1205 tptrace(TPPTsendack, -1, $P.tp_lcredit, $P.tp_sent_uwe, 1206 $P.tp_sent_lcdt, 0); 1207 ENDTRACE 1208 IncPStat($P, tps_n_TMsendack); 1209 (void) tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1210 } 1211; 1212 1213/* TP0 only */ 1214SAME <== TP_OPEN T_USR_rcvd 1215 ($P.tp_class == TP_CLASS_0) 1216 { 1217 if (sbspace(&$P.tp_sock->so_rcv) > 0) 1218 tp0_openflow($P); 1219 } 1220; 1221 1222/* TP4 only */ 1223 /* If old credit was zero, 1224 * we'd better inform other side that we now have space 1225 * But this is not enough. Sender might not yet have 1226 * seen an ack with cdt 0 but it might still think the 1227 * window is closed, so it's going to wait. 1228 * Best to send an ack each time. 1229 * Strictly speaking, this ought to be a function of the 1230 * general ack strategy. 1231 */ 1232SAME <== TP_OPEN T_USR_rcvd 1233 DEFAULT 1234 { 1235 if( trick_hc ) { 1236 SeqNum ack_thresh; 1237 /* 1238 * If the upper window edge has advanced a reasonable 1239 * amount beyond what was known, send an ACK. 1240 * A reasonable amount is 2 packets, unless the max window 1241 * is only 1 or 2 packets, in which case we 1242 * should send an ack for any advance in the upper window edge. 1243 */ 1244 LOCAL_CREDIT($P); 1245 ack_thresh = SEQ_SUB($P, $P.tp_lcredit + $P.tp_rcvnxt, 1246 ($P.tp_maxlcredit > 2 ? 2 : 1)); 1247 if (SEQ_GT($P, ack_thresh, $P.tp_sent_uwe)) { 1248 IncStat(ts_ackreason[_ACK_USRRCV_]); 1249 $P.tp_flags &= ~TPF_DELACK; 1250 return tp_emit(AK_TPDU_type, $P, $P.tp_rcvnxt, 0, MNULL); 1251 } 1252 } 1253 } 1254; 1255 1256/* applicable in TP4, TP0 */ 1257SAME <== TP_REFWAIT [ T_USR_rcvd, T_USR_Xrcvd ] 1258 DEFAULT 1259 /* This happens if other end sent a DR when the user was waiting 1260 * on a receive. 1261 * Processing the DR includes putting us in REFWAIT state. 1262 */ 1263 { 1264 if(trick_hc) 1265 return ECONNABORTED; 1266 } 1267; 1268 1269/* TP0 only */ 1270TP_REFWAIT <== [ TP_OPEN, TP_CRSENT, TP_LISTENING ] T_NETRESET 1271 ( $P.tp_class != TP_CLASS_4 ) 1272 /* 0 or (4 and 0) */ 1273 /* in OPEN class will be 0 or 4 but not both */ 1274 /* in CRSENT or LISTENING it could be in negotiation, hence both */ 1275 /* Actually, this shouldn't ever happen in LISTENING */ 1276 { 1277 ASSERT( $P.tp_state != TP_LISTENING ); 1278 tp_indicate(T_DISCONNECT, $P, ECONNRESET); 1279 tp_soisdisconnected($P); 1280 } 1281; 1282 1283/* TP4: ignore resets */ 1284SAME <== [ TP_OPEN, TP_CRSENT, TP_AKWAIT, 1285 TP_CLOSING, TP_LISTENING ] T_NETRESET 1286 DEFAULT 1287 NULLACTION 1288; 1289 1290/* applicable in TP4, TP0 */ 1291SAME <== [ TP_CLOSED, TP_REFWAIT ] T_NETRESET 1292 DEFAULT 1293 NULLACTION 1294; 1295 1296/* C'EST TOUT */ 1297