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