/* $NetBSD: tp_driver.c,v 1.20 2009/03/14 21:04:25 dsl Exp $ */ #include __KERNEL_RCSID(0, "$NetBSD: tp_driver.c,v 1.20 2009/03/14 21:04:25 dsl Exp $"); #include "tp_states.h" static const struct act_ent { int a_newstate; int a_action; } statetable[] = {{ 0, 0 }, #include "tp_states.init" }; /* @(#)tp.trans 8.1 (Berkeley) 6/10/93 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRIVERTRACE TPPTdriver #define sbwakeup(sb, dir) sowakeup(p->tp_sock, sb, dir); #define MCPY(d, w) (d ? m_copym(d, 0, (int)M_COPYALL, w): 0) static int trick_hc = 1; #include "tp_events.h" static int _Xebec_action (int, struct tp_event *, struct tp_pcb *); static int _Xebec_index (struct tp_event *, struct tp_pcb *); static int _Xebec_action(int a, struct tp_event *e, struct tp_pcb *p) { int error; struct mbuf *data = NULL; int doack; struct socket *so = p->tp_sock; struct sockbuf *sb; int timo; switch (a) { case -1: return tp_protocol_error(e, p); case 0x1: (void) tp_emit(DC_TPDU_type, p, 0, 0, NULL); break; case 0x2: #ifdef TP_DEBUG if (e->ev_number != AK_TPDU) printf("TPDU 0x%x in REFWAIT!!!!\n", e->ev_number); #endif /* TP_DEBUG */ break; case 0x3: /* oh, man is this grotesque or what? */ (void) tp_goodack(p, e->ev_union.EV_AK_TPDU.e_cdt, e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_subseq); /* * but it's necessary because this pseudo-ack may * happen before the CC arrives, but we HAVE to * adjust the snduna as a result of the ack, WHENEVER * it arrives */ break; case 0x4: tp_detach(p); break; case 0x5: p->tp_refstate = REF_OPEN; /* has timers ??? */ break; case 0x6: #ifdef TPPT if (tp_traceflags[D_CONN]) tptrace(TPPTmisc, "CR datalen data", e->ev_union.EV_CR_TPDU.e_datalen, e->ev_union.EV_CR_TPDU.e_data, 0, 0); #endif #ifdef ARGO_DEBUG if (argo_debug[D_CONN]) { printf("CR datalen 0x%x data %p", e->ev_union.EV_CR_TPDU.e_datalen, e->ev_union.EV_CR_TPDU.e_data); } #endif p->tp_refstate = REF_OPEN; /* has timers */ p->tp_fcredit = e->ev_union.EV_CR_TPDU.e_cdt; if (e->ev_union.EV_CR_TPDU.e_datalen > 0) { /* n/a for class 0 */ ASSERT(p->tp_Xrcv.sb_cc == 0); sbappendrecord(&p->tp_Xrcv, e->ev_union.EV_CR_TPDU.e_data); e->ev_union.EV_CR_TPDU.e_data = NULL; } break; case 0x7: IncStat(ts_tp0_conn); #ifdef TPPT if (tp_traceflags[D_CONN]) tptrace(TPPTmisc, "Confiming", p, 0, 0, 0); #endif #ifdef ARGO_DEBUG if (argo_debug[D_CONN]) { printf("Confirming connection: p"); } #endif soisconnected(p->tp_sock); (void) tp_emit(CC_TPDU_type, p, 0, 0, NULL); p->tp_fcredit = 1; break; case 0x8: IncStat(ts_tp4_conn); /* even though not quite open */ #ifdef TPPT if (tp_traceflags[D_CONN]) tptrace(TPPTmisc, "Confiming", p, 0, 0, 0); #endif #ifdef ARGO_DEBUG if (argo_debug[D_CONN]) { printf("Confirming connection: p"); } #endif tp_getoptions(p); soisconnecting(p->tp_sock); if ((p->tp_rx_strat & TPRX_FASTSTART) && (p->tp_fcredit > 0)) p->tp_cong_win = p->tp_fcredit * p->tp_l_tpdusize; p->tp_retrans = p->tp_Nretrans; tp_ctimeout(p, TM_retrans, (int) p->tp_cc_ticks); break; case 0x9: #ifdef ARGO_DEBUG if (argo_debug[D_CONN]) { printf("event: CR_TPDU emit CC failed done "); } #endif soisdisconnected(p->tp_sock); tp_recycle_tsuffix(p); tp_freeref(p->tp_lref); tp_detach(p); break; case 0xa: #ifdef TPPT if (tp_traceflags[D_CONN]) tptrace(TPPTmisc, "T_CONN_req flags ucddata", (int) p->tp_flags, p->tp_ucddata, 0, 0); #endif data = MCPY(p->tp_ucddata, M_WAIT); if (data) { #ifdef ARGO_DEBUG if (argo_debug[D_CONN]) { printf("T_CONN_req.trans m_copy cc %p\n", p->tp_ucddata); dump_mbuf(data, "sosnd @ T_CONN_req"); } #endif } if ((error = tp_emit(CR_TPDU_type, p, 0, 0, data)) != 0) return error; /* driver WON'T change state; * will return error */ p->tp_refstate = REF_OPEN; /* has timers */ if (p->tp_class != TP_CLASS_0) { p->tp_retrans = p->tp_Nretrans; tp_ctimeout(p, TM_retrans, (int) p->tp_cr_ticks); } break; case 0xb: sbflush(&p->tp_Xrcv); /* purge non-delivered data * data */ if (e->ev_union.EV_DR_TPDU.e_datalen > 0) { sbappendrecord(&p->tp_Xrcv, e->ev_union.EV_DR_TPDU.e_data); e->ev_union.EV_DR_TPDU.e_data = NULL; } if (p->tp_state == TP_OPEN) tp_indicate(T_DISCONNECT, p, 0); else { int so_error = ECONNREFUSED; if (e->ev_union.EV_DR_TPDU.e_reason != (E_TP_NO_SESSION ^ TP_ERROR_MASK) && e->ev_union.EV_DR_TPDU.e_reason != (E_TP_NO_CR_ON_NC ^ TP_ERROR_MASK) && e->ev_union.EV_DR_TPDU.e_reason != (E_TP_REF_OVERFLOW ^ TP_ERROR_MASK)) so_error = ECONNABORTED; tp_indicate(T_DISCONNECT, p, so_error); } tp_soisdisconnected(p); if (p->tp_class != TP_CLASS_0) { if (p->tp_state == TP_OPEN) { tp_euntimeout(p, TM_data_retrans);/* all */ tp_cuntimeout(p, TM_retrans); tp_cuntimeout(p, TM_inact); tp_cuntimeout(p, TM_sendack); p->tp_flags &= ~TPF_DELACK; } tp_cuntimeout(p, TM_retrans); if (e->ev_union.EV_DR_TPDU.e_sref != 0) (void) tp_emit(DC_TPDU_type, p, 0, 0, NULL); } break; case 0xc: if (e->ev_union.EV_DR_TPDU.e_sref != 0) (void) tp_emit(DC_TPDU_type, p, 0, 0, NULL); /* * reference timer already set - reset it to be safe * (???) */ tp_euntimeout(p, TM_reference); /* all */ tp_etimeout(p, TM_reference, (int) p->tp_refer_ticks); break; case 0xd: tp_cuntimeout(p, TM_retrans); tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason); tp_soisdisconnected(p); break; case 0xe: tp_cuntimeout(p, TM_retrans); tp_soisdisconnected(p); break; case 0xf: tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason); tp_cuntimeout(p, TM_retrans); tp_soisdisconnected(p); break; case 0x10: tp_cuntimeout(p, TM_retrans); tp_soisdisconnected(p); break; case 0x11: /* don't ask me why we have to do this - spec * says so */ (void) tp_emit(DR_TPDU_type, p, 0, E_TP_NO_SESSION, NULL); /* don't bother with retransmissions of the DR */ break; case 0x12: tp_soisdisconnecting(p->tp_sock); tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason); tp_soisdisconnected(p); tp_netcmd(p, CONN_CLOSE); break; case 0x13: if (p->tp_state == TP_OPEN) { tp_euntimeout(p, TM_data_retrans); /* all */ tp_cuntimeout(p, TM_inact); tp_cuntimeout(p, TM_sendack); } tp_soisdisconnecting(p->tp_sock); tp_indicate(ER_TPDU, p, e->ev_union.EV_ER_TPDU.e_reason); p->tp_retrans = p->tp_Nretrans; tp_ctimeout(p, TM_retrans, (int) p->tp_dr_ticks); (void) tp_emit(DR_TPDU_type, p, 0, E_TP_PROTO_ERR, NULL); break; case 0x14: tp_cuntimeout(p, TM_retrans); IncStat(ts_tp0_conn); p->tp_fcredit = 1; soisconnected(p->tp_sock); break; case 0x15: #ifdef ARGO_DEBUG if (argo_debug[D_CONN]) { printf("trans: CC_TPDU in CRSENT state flags 0x%x\n", (int) p->tp_flags); } #endif IncStat(ts_tp4_conn); p->tp_fref = e->ev_union.EV_CC_TPDU.e_sref; p->tp_fcredit = e->ev_union.EV_CC_TPDU.e_cdt; if ((p->tp_rx_strat & TPRX_FASTSTART) && (e->ev_union.EV_CC_TPDU.e_cdt > 0)) p->tp_cong_win = e->ev_union.EV_CC_TPDU.e_cdt * p->tp_l_tpdusize; tp_getoptions(p); tp_cuntimeout(p, TM_retrans); if (p->tp_ucddata) { #ifdef ARGO_DEBUG if (argo_debug[D_CONN]) { printf("dropping user connect data cc 0x%x\n", p->tp_ucddata->m_len); } #endif m_freem(p->tp_ucddata); p->tp_ucddata = 0; } soisconnected(p->tp_sock); if (e->ev_union.EV_CC_TPDU.e_datalen > 0) { ASSERT(p->tp_Xrcv.sb_cc == 0); /* should be empty */ sbappendrecord(&p->tp_Xrcv, e->ev_union.EV_CC_TPDU.e_data); e->ev_union.EV_CC_TPDU.e_data = NULL; } (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, NULL); tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); break; case 0x16: IncStat(ts_retrans_cr); p->tp_cong_win = 1 * p->tp_l_tpdusize; data = MCPY(p->tp_ucddata, M_NOWAIT); if (p->tp_ucddata) { #ifdef ARGO_DEBUG if (argo_debug[D_CONN]) { printf("TM_retrans.trans m_copy cc %p\n", data); dump_mbuf(p->tp_ucddata, "sosnd @ TM_retrans"); } #endif if (data == NULL) return ENOBUFS; } p->tp_retrans--; if ((error = tp_emit(CR_TPDU_type, p, 0, 0, data)) != 0) { p->tp_sock->so_error = error; } tp_ctimeout(p, TM_retrans, (int) p->tp_cr_ticks); break; case 0x17: IncStat(ts_conn_gaveup); p->tp_sock->so_error = ETIMEDOUT; tp_indicate(T_DISCONNECT, p, ETIMEDOUT); tp_soisdisconnected(p); break; case 0x18: data = MCPY(p->tp_ucddata, M_WAIT); if ((error = tp_emit(CC_TPDU_type, p, 0, 0, data)) != 0) { p->tp_sock->so_error = error; } p->tp_retrans = p->tp_Nretrans; tp_ctimeout(p, TM_retrans, (int) p->tp_cc_ticks); break; case 0x19: /* * Get rid of any confirm or connect data, so that if we * crash or close, it isn't thought of as disconnect data. */ if (p->tp_ucddata) { m_freem(p->tp_ucddata); p->tp_ucddata = 0; } tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); tp_cuntimeout(p, TM_retrans); soisconnected(p->tp_sock); tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); /* * see also next 2 transitions, if you make any * changes */ doack = tp_stash(p, e); #ifdef ARGO_DEBUG if (argo_debug[D_DATA]) { printf("tp_stash returns %d\n", doack); } #endif if (doack) { (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, NULL); tp_ctimeout(p, TM_sendack, (int) p->tp_keepalive_ticks); } else tp_ctimeout(p, TM_sendack, (int) p->tp_sendack_ticks); #ifdef ARGO_DEBUG if (argo_debug[D_DATA]) { printf("after stash calling sbwakeup\n"); } #endif break; case 0x1a: tp0_stash(p, e); sbwakeup(&p->tp_sock->so_rcv, POLL_IN); #ifdef ARGO_DEBUG if (argo_debug[D_DATA]) { printf("after stash calling sbwakeup\n"); } #endif break; case 0x1b: tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); sbwakeup(&p->tp_sock->so_rcv, POLL_IN); doack = tp_stash(p, e); #ifdef ARGO_DEBUG if (argo_debug[D_DATA]) { printf("tp_stash returns %d\n", doack); } #endif if (doack) (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, NULL); else tp_ctimeout_MIN(p, TM_sendack, (int) p->tp_sendack_ticks); #ifdef ARGO_DEBUG if (argo_debug[D_DATA]) { printf("after stash calling sbwakeup\n"); } #endif break; case 0x1c: #ifdef TPPT if (tp_traceflags[D_DATA]) tptrace(TPPTmisc, "NIW seq rcvnxt lcredit ", e->ev_union.EV_DT_TPDU.e_seq, p->tp_rcvnxt, p->tp_lcredit, 0); #endif IncStat(ts_dt_niw); m_freem(e->ev_union.EV_DT_TPDU.e_data); tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, NULL); break; case 0x1d: if (p->tp_ucddata) { m_freem(p->tp_ucddata); p->tp_ucddata = 0; } (void) tp_goodack(p, e->ev_union.EV_AK_TPDU.e_cdt, e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_subseq); tp_cuntimeout(p, TM_retrans); soisconnected(p->tp_sock); #ifdef TPPT if (tp_traceflags[D_CONN]) { struct socket *so = p->tp_sock; tptrace(TPPTmisc, "called sosiconn: so so_state rcv.sb_sel rcv.sb_flags", so, so->so_state, so->so_rcv.sb_sel, so->so_rcv.sb_flags); tptrace(TPPTmisc, "called sosiconn 2: so_qlen so_error so_rcv.sb_cc so_head", so->so_qlen, so->so_error, so->so_rcv.sb_cc, so->so_head); } #endif tp_ctimeout(p, TM_sendack, (int) p->tp_keepalive_ticks); tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); break; case 0x1e: if (p->tp_state == TP_AKWAIT) { if (p->tp_ucddata) { m_freem(p->tp_ucddata); p->tp_ucddata = 0; } tp_cuntimeout(p, TM_retrans); soisconnected(p->tp_sock); tp_ctimeout(p, TM_sendack, (int) p->tp_keepalive_ticks); tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); } #ifdef TPPT if (tp_traceflags[D_XPD]) { tptrace(TPPTmisc, "XPD tpdu accepted Xrcvnxt, " "e_seq datalen m_len\n", p->tp_Xrcvnxt, e->ev_union.EV_XPD_TPDU.e_seq, e->ev_union.EV_XPD_TPDU.e_datalen, e->ev_union.EV_XPD_TPDU.e_data->m_len); } #endif p->tp_sock->so_state |= SS_RCVATMARK; e->ev_union.EV_XPD_TPDU.e_data->m_flags |= M_EOR; sbinsertoob(&p->tp_Xrcv, e->ev_union.EV_XPD_TPDU.e_data); #ifdef ARGO_DEBUG if (argo_debug[D_XPD]) { dump_mbuf(e->ev_union.EV_XPD_TPDU.e_data, "XPD TPDU: tp_Xrcv"); } #endif tp_indicate(T_XDATA, p, 0); sbwakeup(&p->tp_Xrcv, POLL_IN); (void) tp_emit(XAK_TPDU_type, p, p->tp_Xrcvnxt, 0, NULL); SEQ_INC(p, p->tp_Xrcvnxt); break; case 0x1f: if (p->tp_Xrcv.sb_cc == 0) { /* kludge for select(): */ /* p->tp_sock->so_state &= ~SS_OOBAVAIL; */ } break; case 0x20: #ifdef TPPT if (tp_traceflags[D_XPD]) tptrace(TPPTmisc, "XPD tpdu niw (Xrcvnxt, e_seq) or not cdt (cc)\n", p->tp_Xrcvnxt, e->ev_union.EV_XPD_TPDU.e_seq, p->tp_Xrcv.sb_cc, 0); #endif if (p->tp_Xrcvnxt != e->ev_union.EV_XPD_TPDU.e_seq) IncStat(ts_xpd_niw); if (p->tp_Xrcv.sb_cc) { /* might as well kick 'em again */ tp_indicate(T_XDATA, p, 0); IncStat(ts_xpd_dup); } m_freem(e->ev_union.EV_XPD_TPDU.e_data); tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); /* * don't send an xack because the xak gives "last one * received", not "next one i expect" (dumb) */ break; case 0x21: /* detach from parent socket so it can finish closing */ if (so->so_head) { if (!soqremque(so, 0) && !soqremque(so, 1)) panic("tp: T_DETACH"); so->so_head = 0; } tp_soisdisconnecting(p->tp_sock); tp_netcmd(p, CONN_CLOSE); tp_soisdisconnected(p); break; case 0x22: /* detach from parent socket so it can finish closing */ if (so->so_head) { if (!soqremque(so, 0) && !soqremque(so, 1)) panic("tp: T_DETACH"); so->so_head = 0; } if (p->tp_state != TP_CLOSING) { tp_soisdisconnecting(p->tp_sock); data = MCPY(p->tp_ucddata, M_NOWAIT); (void) tp_emit(DR_TPDU_type, p, 0, E_TP_NORMAL_DISC, data); p->tp_retrans = p->tp_Nretrans; tp_ctimeout(p, TM_retrans, (int) p->tp_dr_ticks); } break; case 0x23: tp_soisdisconnecting(p->tp_sock); tp_netcmd(p, CONN_CLOSE); tp_soisdisconnected(p); break; case 0x24: data = MCPY(p->tp_ucddata, M_WAIT); if (p->tp_state == TP_OPEN) { tp_euntimeout(p, TM_data_retrans); /* all */ tp_cuntimeout(p, TM_inact); tp_cuntimeout(p, TM_sendack); p->tp_flags &= ~TPF_DELACK; } if (data) { #ifdef ARGO_DEBUG if (argo_debug[D_CONN]) { printf("T_DISC_req.trans tp_ucddata %p\n", p->tp_ucddata); dump_mbuf(data, "ucddata @ T_DISC_req"); } #endif } tp_soisdisconnecting(p->tp_sock); p->tp_retrans = p->tp_Nretrans; tp_ctimeout(p, TM_retrans, (int) p->tp_dr_ticks); if (trick_hc) return tp_emit(DR_TPDU_type, p, 0, e->ev_union.EV_REQ_TPDU.e_reason, data); break; case 0x25: data = MCPY(p->tp_ucddata, M_WAIT); IncStat(ts_retrans_cc); p->tp_retrans--; p->tp_cong_win = 1 * p->tp_l_tpdusize; if ((error = tp_emit(CC_TPDU_type, p, 0, 0, data)) != 0) p->tp_sock->so_error = error; tp_ctimeout(p, TM_retrans, (int) p->tp_cc_ticks); break; case 0x26: IncStat(ts_conn_gaveup); tp_soisdisconnecting(p->tp_sock); p->tp_sock->so_error = ETIMEDOUT; tp_indicate(T_DISCONNECT, p, ETIMEDOUT); (void) tp_emit(DR_TPDU_type, p, 0, E_TP_CONGEST, NULL); p->tp_retrans = p->tp_Nretrans; tp_ctimeout(p, TM_retrans, (int) p->tp_dr_ticks); break; case 0x27: tp_euntimeout(p, TM_data_retrans); /* all */ tp_cuntimeout(p, TM_inact); tp_cuntimeout(p, TM_sendack); IncStat(ts_conn_gaveup); tp_soisdisconnecting(p->tp_sock); p->tp_sock->so_error = ETIMEDOUT; tp_indicate(T_DISCONNECT, p, ETIMEDOUT); (void) tp_emit(DR_TPDU_type, p, 0, E_TP_CONGEST_2, NULL); p->tp_retrans = p->tp_Nretrans; tp_ctimeout(p, TM_retrans, (int) p->tp_dr_ticks); break; case 0x28: p->tp_cong_win = 1 * p->tp_l_tpdusize; /* resume XPD */ if (p->tp_Xsnd.sb_mb) { struct mbuf *m = m_copy(p->tp_Xsnd.sb_mb, 0, (int) p->tp_Xsnd.sb_cc); int shift; #ifdef TPPT if (tp_traceflags[D_XPD]) { tptrace(TPPTmisc, "XPD retrans: Xuna Xsndnxt sndnxt snduna", p->tp_Xuna, p->tp_Xsndnxt, p->tp_sndnxt, p->tp_snduna); } #endif #ifdef ARGO_DEBUG if (argo_debug[D_XPD]) { dump_mbuf(m, "XPD retrans emitting M"); } #endif IncStat(ts_retrans_xpd); p->tp_retrans--; shift = max(p->tp_Nretrans - p->tp_retrans, 6); (void) tp_emit(XPD_TPDU_type, p, p->tp_Xuna, 1, m); tp_ctimeout(p, TM_retrans, ((int) p->tp_dt_ticks) << shift); } break; case 0x29: p->tp_rxtshift++; (void) tp_data_retrans(p); break; case 0x2a: p->tp_retrans--; (void) tp_emit(DR_TPDU_type, p, 0, E_TP_DR_NO_REAS, NULL); IncStat(ts_retrans_dr); tp_ctimeout(p, TM_retrans, (int) p->tp_dr_ticks); break; case 0x2b: p->tp_sock->so_error = ETIMEDOUT; p->tp_refstate = REF_FROZEN; tp_recycle_tsuffix(p); tp_etimeout(p, TM_reference, (int) p->tp_refer_ticks); break; case 0x2c: tp_freeref(p->tp_lref); tp_detach(p); break; case 0x2d: if (p->tp_class != TP_CLASS_0) { tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); if (e->ev_number == CC_TPDU) (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, NULL); } /* * ignore it if class 0 - state tables are blank for * this */ break; case 0x2e: #ifdef TPPT if (tp_traceflags[D_DATA]) tptrace(TPPTmisc, "T_DATA_req sndnxt snduna fcredit, tpcb", p->tp_sndnxt, p->tp_snduna, p->tp_fcredit, p); #endif tp_send(p); break; case 0x2f: error = 0; /* resume XPD */ if (p->tp_Xsnd.sb_mb) { struct mbuf *m = m_copy(p->tp_Xsnd.sb_mb, 0, (int) p->tp_Xsnd.sb_cc); /* * m_copy doesn't preserve the m_xlink field, * but at this pt. that doesn't matter */ #ifdef TPPT if (tp_traceflags[D_XPD]) tptrace(TPPTmisc, "XPD req: Xuna Xsndnxt sndnxt snduna", p->tp_Xuna, p->tp_Xsndnxt, p->tp_sndnxt, p->tp_snduna); #endif #ifdef ARGO_DEBUG if (argo_debug[D_XPD]) { printf("T_XPD_req: sb_cc 0x%lx\n", p->tp_Xsnd.sb_cc); dump_mbuf(m, "XPD req emitting M"); } #endif error = tp_emit(XPD_TPDU_type, p, p->tp_Xuna, 1, m); p->tp_retrans = p->tp_Nretrans; tp_ctimeout(p, TM_retrans, (int) p->tp_rxtcur); SEQ_INC(p, p->tp_Xsndnxt); } if (trick_hc) return error; break; case 0x30: sb = &p->tp_sock->so_snd; #ifdef ARGO_DEBUG if (argo_debug[D_ACKRECV]) { printf("GOOD ACK seq 0x%x cdt 0x%x\n", e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_cdt); } #endif if (p->tp_class != TP_CLASS_0) { tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); } sbwakeup(sb, POLL_OUT); #ifdef ARGO_DEBUG if (argo_debug[D_ACKRECV]) { printf("GOOD ACK new sndnxt 0x%x\n", p->tp_sndnxt); } #endif break; case 0x31: #ifdef TPPT if (tp_traceflags[D_ACKRECV]) tptrace(TPPTmisc, "BOGUS ACK fcc_present, tp_r_subseq e_subseq", e->ev_union.EV_AK_TPDU.e_fcc_present, p->tp_r_subseq, e->ev_union.EV_AK_TPDU.e_subseq, 0); #endif if (p->tp_class != TP_CLASS_0) { if (!e->ev_union.EV_AK_TPDU.e_fcc_present) { /* send ACK with FCC */ IncStat(ts_ackreason[_ACK_FCC_]); (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 1, NULL); } tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); } break; case 0x32: tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); tp_cuntimeout(p, TM_retrans); sbwakeup(&p->tp_sock->so_snd, POLL_OUT); /* resume normal data */ tp_send(p); break; case 0x33: #ifdef TPPT if (tp_traceflags[D_ACKRECV]) tptrace(TPPTmisc, "BOGUS XACK eventtype ", e->ev_number, 0, 0, 0); #endif if (p->tp_class != TP_CLASS_0) { tp_ctimeout(p, TM_inact, (int) p->tp_inact_ticks); } break; case 0x34: #ifdef TPPT if (tp_traceflags[D_TIMER]) tptrace(TPPTsendack, -1, p->tp_lcredit, p->tp_sent_uwe, p->tp_sent_lcdt, 0); #endif IncPStat(p, tps_n_TMsendack); (void) tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, NULL); if (p->tp_fcredit == 0) { if (p->tp_rxtshift < TP_MAXRXTSHIFT) p->tp_rxtshift++; timo = (p->tp_dt_ticks) << p->tp_rxtshift; } else timo = p->tp_sendack_ticks; tp_ctimeout(p, TM_sendack, timo); break; case 0x35: if (sbspace(&p->tp_sock->so_rcv) > 0) tp0_openflow(p); break; case 0x36: if (trick_hc) { SeqNum ack_thresh; /* * If the upper window edge has advanced a reasonable * amount beyond what was known, send an ACK. * A reasonable amount is 2 packets, unless the max window * is only 1 or 2 packets, in which case we * should send an ack for any advance in the upper window edge. */ LOCAL_CREDIT(p); ack_thresh = SEQ_SUB(p, p->tp_lcredit + p->tp_rcvnxt, (p->tp_maxlcredit > 2 ? 2 : 1)); if (SEQ_GT(p, ack_thresh, p->tp_sent_uwe)) { IncStat(ts_ackreason[_ACK_USRRCV_]); p->tp_flags &= ~TPF_DELACK; return tp_emit(AK_TPDU_type, p, p->tp_rcvnxt, 0, NULL); } } break; case 0x37: if (trick_hc) return ECONNABORTED; break; case 0x38: ASSERT(p->tp_state != TP_LISTENING); tp_indicate(T_DISCONNECT, p, ECONNRESET); tp_soisdisconnected(p); break; } return 0; } static int _Xebec_index(struct tp_event *e, struct tp_pcb *p) { switch ((e->ev_number << 4) + (p->tp_state)) { case 0x12: if (p->tp_retrans > 0) return 0x1e; else return 0x1f; case 0x13: if (p->tp_retrans > 0) return 0x2f; else return 0x30; case 0x14: if (p->tp_retrans > 0) return 0x32; else return 0x31; case 0x15: if (p->tp_retrans > 0) return 0x34; else return 0x35; case 0x54: if (p->tp_rxtshift < TP_NRETRANS) return 0x33; else return 0x31; case 0x64: if (p->tp_class == TP_CLASS_0) return 0x1a; else return 0x1b; case 0x77: if (p->tp_class == TP_CLASS_0) return 0xd; else return 0xe; case 0x86: if (e->ev_union.EV_DR_TPDU.e_sref != 0) return 0x2; else return 0x3; case 0xa2: if (p->tp_class == TP_CLASS_0) return 0x1c; else return 0x1d; case 0xb2: if (p->tp_class == TP_CLASS_0) return 0x5; else return 0x0; case 0xb4: if (tp_goodack(p, e->ev_union.EV_AK_TPDU.e_cdt, e->ev_union.EV_AK_TPDU.e_seq, e->ev_union.EV_AK_TPDU.e_subseq)) return 0x3a; else return 0x3b; case 0xc3: if (IN_RWINDOW(p, e->ev_union.EV_DT_TPDU.e_seq, p->tp_rcvnxt, SEQ(p, p->tp_rcvnxt + p->tp_lcredit))) return 0x21; else return 0x24; case 0xc4: if (p->tp_class == TP_CLASS_0) return 0x22; else if (IN_RWINDOW(p, e->ev_union.EV_DT_TPDU.e_seq, p->tp_rcvnxt, SEQ(p, p->tp_rcvnxt + p->tp_lcredit))) return 0x23; else return 0x25; case 0xd3: if (p->tp_Xrcvnxt == e->ev_union.EV_XPD_TPDU.e_seq) return 0x27; else return 0x2a; case 0xd4: if (p->tp_Xrcvnxt == e->ev_union.EV_XPD_TPDU.e_seq) return 0x27; else return 0x29; case 0xe4: if (tp_goodXack(p, e->ev_union.EV_XAK_TPDU.e_seq)) return 0x3c; else return 0x3d; case 0x102: if (p->tp_class == TP_CLASS_0) return 0x2d; else return 0x2e; case 0x104: if (p->tp_class == TP_CLASS_0) return 0x2d; else return 0x2e; case 0x144: if (p->tp_class == TP_CLASS_0) return 0x3f; else return 0x40; case 0x162: if (p->tp_class == TP_CLASS_0) return 0x2b; else return 0x2c; case 0x172: if (p->tp_class != TP_CLASS_4) return 0x42; else return 0x46; case 0x174: if (p->tp_class != TP_CLASS_4) return 0x42; else return 0x47; case 0x177: if (p->tp_class != TP_CLASS_4) return 0x42; else return 0x43; case 0x188: if (p->tp_class == TP_CLASS_0) return 0xf; else if (tp_emit(CC_TPDU_type, p, 0, 0, MCPY(p->tp_ucddata, M_NOWAIT)) == 0) return 0x10; else return 0x11; default: return 0; } /* end switch */ } /* _Xebec_index() */ static const int inx[26][9] = { {0, 0, 0, 0, 0, 0, 0, 0, 0,}, {0x0, 0x0, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0,}, {0x0, 0x0, -1, -1, -1, -1, 0x0, 0x0, 0x0,}, {0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x0, 0x0,}, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,}, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0,}, {0x0, 0x0, 0x0, 0x0, -1, 0x0, 0x0, 0x0, 0x0,}, {0x0, 0x7, 0x15, 0x1b, -1, 0x17, 0x3, 0xa, 0x0,}, {0x0, 0x19, 0x6, 0x20, 0x37, 0x8, 0x3, -1, 0x0,}, {0x0, 0x14, 0x13, 0x13, 0x13, 0x16, -1, 0xa, 0x0,}, {0x0, 0x7, 0x6, 0x1, 0x9, 0x18, 0x3, 0xa, 0x0,}, {0x0, 0x19, -1, 0x1, 0x37, 0x8, 0x3, 0xa, 0x0,}, {0x0, 0x7, -1, 0x26, -1, 0x8, 0x3, 0xa, 0x0,}, {0x0, 0x7, 0x6, -1, -1, 0x8, 0x3, 0xa, 0x0,}, {0x0, 0x7, 0x6, -1, -1, 0x8, 0x3, 0xa, 0x0,}, {0x0, 0x7, 0x6, 0x1, -1, 0x8, 0x3, 0xa, 0x0,}, {0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,}, {0x0, 0x0, -1, 0x2e, -1, 0x0, 0x4, 0x0, 0x2e,}, {0x0, 0xb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,}, {0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0,}, {0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0,}, {0x0, 0x0, 0x0, 0x0, -1, 0x0, 0x41, 0x0, 0x0,}, {0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x41, 0x0, 0x0,}, {0x0, 0xc, -1, 0x2c, 0x0, 0x2c, 0x4, 0xc, 0x2c,}, {0x0, 0x49, -1, 0x45, -1, 0x44, 0x48, -1, 0x0,}, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -1,}, }; int tp_driver(struct tp_pcb *p, struct tp_event *e) { int index, error = 0; const struct act_ent *a; static const struct act_ent erroraction = {0, -1}; index = inx[1 + e->ev_number][p->tp_state]; if (index < 0) index = _Xebec_index(e, p); if (index == 0) { a = &erroraction; } else a = &statetable[index]; if (a->a_action) error = _Xebec_action(a->a_action, e, p); #ifdef TPPT if (tp_traceflag[D_DRIVER]) tptrace(DRIVERTRACE, a->a_newstate, p->tp_state, e->ev_number, a->a_action, 0); #endif if (error == 0) p->tp_state = a->a_newstate; return error; }