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