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