spx_reass.c revision 139589
111819Sjulian/* 2139485Srwatson * Copyright (c) 2004 Robert N. M. Watson 311819Sjulian * Copyright (c) 1995, Mike Mitchell 411819Sjulian * Copyright (c) 1984, 1985, 1986, 1987, 1993 511819Sjulian * The Regents of the University of California. All rights reserved. 611819Sjulian * 711819Sjulian * Redistribution and use in source and binary forms, with or without 811819Sjulian * modification, are permitted provided that the following conditions 911819Sjulian * are met: 1011819Sjulian * 1. Redistributions of source code must retain the above copyright 1111819Sjulian * notice, this list of conditions and the following disclaimer. 1211819Sjulian * 2. Redistributions in binary form must reproduce the above copyright 1311819Sjulian * notice, this list of conditions and the following disclaimer in the 1411819Sjulian * documentation and/or other materials provided with the distribution. 1511819Sjulian * 3. All advertising materials mentioning features or use of this software 1611819Sjulian * must display the following acknowledgement: 1711819Sjulian * This product includes software developed by the University of 1811819Sjulian * California, Berkeley and its contributors. 1911819Sjulian * 4. Neither the name of the University nor the names of its contributors 2011819Sjulian * may be used to endorse or promote products derived from this software 2111819Sjulian * without specific prior written permission. 2211819Sjulian * 2311819Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2411819Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2511819Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2611819Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2711819Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2811819Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2911819Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3011819Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3111819Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3211819Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3311819Sjulian * SUCH DAMAGE. 3411819Sjulian * 3512057Sjulian * @(#)spx_usrreq.h 3611819Sjulian */ 3711819Sjulian 38116189Sobrien#include <sys/cdefs.h> 39116189Sobrien__FBSDID("$FreeBSD: head/sys/netipx/spx_usrreq.c 139589 2005-01-02 15:33:13Z rwatson $"); 40116189Sobrien 4111819Sjulian#include <sys/param.h> 4276166Smarkm#include <sys/lock.h> 4329024Sbde#include <sys/malloc.h> 4411819Sjulian#include <sys/mbuf.h> 4576166Smarkm#include <sys/mutex.h> 4625345Sjhay#include <sys/proc.h> 4711819Sjulian#include <sys/protosw.h> 4895759Stanimura#include <sys/signalvar.h> 4911819Sjulian#include <sys/socket.h> 5011819Sjulian#include <sys/socketvar.h> 5195759Stanimura#include <sys/sx.h> 5295759Stanimura#include <sys/systm.h> 5311819Sjulian 5411819Sjulian#include <net/route.h> 5511819Sjulian#include <netinet/tcp_fsm.h> 5611819Sjulian 5711819Sjulian#include <netipx/ipx.h> 5811819Sjulian#include <netipx/ipx_pcb.h> 5911819Sjulian#include <netipx/ipx_var.h> 6011819Sjulian#include <netipx/spx.h> 6195759Stanimura#include <netipx/spx_debug.h> 6211819Sjulian#include <netipx/spx_timer.h> 6311819Sjulian#include <netipx/spx_var.h> 6411819Sjulian 6511819Sjulian/* 6611819Sjulian * SPX protocol implementation. 6711819Sjulian */ 6833181Seivindstatic u_short spx_iss; 6933181Seivindstatic u_short spx_newchecks[50]; 7033181Seivindstatic int spx_hardnosed; 7133181Seivindstatic int spx_use_delack = 0; 7233181Seivindstatic int traceallspxs = 0; 7333181Seivindstatic struct spx spx_savesi; 7433181Seivindstatic struct spx_istat spx_istat; 7511819Sjulian 7625652Sjhay/* Following was struct spxstat spxstat; */ 77139584Srwatson#ifndef spxstat 7825652Sjhay#define spxstat spx_istat.newstats 79139584Srwatson#endif 8011819Sjulian 81132045Srwatsonstatic const int spx_backoff[SPX_MAXRXTSHIFT+1] = 8225652Sjhay { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 }; 8311819Sjulian 8425652Sjhaystatic struct spxpcb *spx_close(struct spxpcb *cb); 8525652Sjhaystatic struct spxpcb *spx_disconnect(struct spxpcb *cb); 8625652Sjhaystatic struct spxpcb *spx_drop(struct spxpcb *cb, int errno); 8725652Sjhaystatic int spx_output(struct spxpcb *cb, struct mbuf *m0); 8825652Sjhaystatic int spx_reass(struct spxpcb *cb, struct spx *si); 8925652Sjhaystatic void spx_setpersist(struct spxpcb *cb); 9025652Sjhaystatic void spx_template(struct spxpcb *cb); 9125652Sjhaystatic struct spxpcb *spx_timers(struct spxpcb *cb, int timer); 9225652Sjhaystatic struct spxpcb *spx_usrclosed(struct spxpcb *cb); 9325652Sjhay 9424659Sjhaystatic int spx_usr_abort(struct socket *so); 9528270Swollmanstatic int spx_accept(struct socket *so, struct sockaddr **nam); 9683366Sjulianstatic int spx_attach(struct socket *so, int proto, struct thread *td); 9783366Sjulianstatic int spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td); 9828270Swollmanstatic int spx_connect(struct socket *so, struct sockaddr *nam, 9983366Sjulian struct thread *td); 10024659Sjhaystatic int spx_detach(struct socket *so); 10124659Sjhaystatic int spx_usr_disconnect(struct socket *so); 10283366Sjulianstatic int spx_listen(struct socket *so, struct thread *td); 10324659Sjhaystatic int spx_rcvd(struct socket *so, int flags); 10424659Sjhaystatic int spx_rcvoob(struct socket *so, struct mbuf *m, int flags); 10524659Sjhaystatic int spx_send(struct socket *so, int flags, struct mbuf *m, 106139584Srwatson struct sockaddr *addr, struct mbuf *control, 10783366Sjulian struct thread *td); 10824659Sjhaystatic int spx_shutdown(struct socket *so); 10983366Sjulianstatic int spx_sp_attach(struct socket *so, int proto, struct thread *td); 11024659Sjhay 11124659Sjhaystruct pr_usrreqs spx_usrreqs = { 112137386Sphk .pru_abort = spx_usr_abort, 113137386Sphk .pru_accept = spx_accept, 114137386Sphk .pru_attach = spx_attach, 115137386Sphk .pru_bind = spx_bind, 116137386Sphk .pru_connect = spx_connect, 117137386Sphk .pru_control = ipx_control, 118137386Sphk .pru_detach = spx_detach, 119137386Sphk .pru_disconnect = spx_usr_disconnect, 120137386Sphk .pru_listen = spx_listen, 121137386Sphk .pru_peeraddr = ipx_peeraddr, 122137386Sphk .pru_rcvd = spx_rcvd, 123137386Sphk .pru_rcvoob = spx_rcvoob, 124137386Sphk .pru_send = spx_send, 125137386Sphk .pru_shutdown = spx_shutdown, 126137386Sphk .pru_sockaddr = ipx_sockaddr, 12724659Sjhay}; 12824659Sjhay 12924659Sjhaystruct pr_usrreqs spx_usrreq_sps = { 130137386Sphk .pru_abort = spx_usr_abort, 131137386Sphk .pru_accept = spx_accept, 132137386Sphk .pru_attach = spx_sp_attach, 133137386Sphk .pru_bind = spx_bind, 134137386Sphk .pru_connect = spx_connect, 135137386Sphk .pru_control = ipx_control, 136137386Sphk .pru_detach = spx_detach, 137137386Sphk .pru_disconnect = spx_usr_disconnect, 138137386Sphk .pru_listen = spx_listen, 139137386Sphk .pru_peeraddr = ipx_peeraddr, 140137386Sphk .pru_rcvd = spx_rcvd, 141137386Sphk .pru_rcvoob = spx_rcvoob, 142137386Sphk .pru_send = spx_send, 143137386Sphk .pru_shutdown = spx_shutdown, 144137386Sphk .pru_sockaddr = ipx_sockaddr, 14524659Sjhay}; 14624659Sjhay 14711819Sjulianvoid 14811819Sjulianspx_init() 14911819Sjulian{ 15011819Sjulian 15111819Sjulian spx_iss = 1; /* WRONG !! should fish it out of TODR */ 15211819Sjulian} 15311819Sjulian 15411819Sjulianvoid 15511819Sjulianspx_input(m, ipxp) 15611819Sjulian register struct mbuf *m; 15711819Sjulian register struct ipxpcb *ipxp; 15811819Sjulian{ 15911819Sjulian register struct spxpcb *cb; 16011819Sjulian register struct spx *si = mtod(m, struct spx *); 16111819Sjulian register struct socket *so; 16211819Sjulian int dropsocket = 0; 16311819Sjulian short ostate = 0; 16411819Sjulian 16511819Sjulian spxstat.spxs_rcvtotal++; 166139586Srwatson KASSERT(ipxp != NULL, ("spx_input: NULL ipxpcb")); 16711819Sjulian 16811819Sjulian cb = ipxtospxpcb(ipxp); 16925652Sjhay if (cb == NULL) 17025652Sjhay goto bad; 17111819Sjulian 17211819Sjulian if (m->m_len < sizeof(*si)) { 17325652Sjhay if ((m = m_pullup(m, sizeof(*si))) == NULL) { 17411819Sjulian spxstat.spxs_rcvshort++; 17511819Sjulian return; 17611819Sjulian } 17711819Sjulian si = mtod(m, struct spx *); 17811819Sjulian } 17911819Sjulian si->si_seq = ntohs(si->si_seq); 18011819Sjulian si->si_ack = ntohs(si->si_ack); 18111819Sjulian si->si_alo = ntohs(si->si_alo); 18211819Sjulian 18311819Sjulian so = ipxp->ipxp_socket; 18411819Sjulian 18511819Sjulian if (so->so_options & SO_DEBUG || traceallspxs) { 18611819Sjulian ostate = cb->s_state; 18711819Sjulian spx_savesi = *si; 18811819Sjulian } 18911819Sjulian if (so->so_options & SO_ACCEPTCONN) { 19011819Sjulian struct spxpcb *ocb = cb; 19111819Sjulian 19211819Sjulian so = sonewconn(so, 0); 19325652Sjhay if (so == NULL) { 19411819Sjulian goto drop; 19511819Sjulian } 19611819Sjulian /* 19711819Sjulian * This is ugly, but .... 19811819Sjulian * 19911819Sjulian * Mark socket as temporary until we're 20011819Sjulian * committed to keeping it. The code at 20111819Sjulian * ``drop'' and ``dropwithreset'' check the 20211819Sjulian * flag dropsocket to see if the temporary 20311819Sjulian * socket created here should be discarded. 20411819Sjulian * We mark the socket as discardable until 20511819Sjulian * we're committed to it below in TCPS_LISTEN. 20611819Sjulian */ 20711819Sjulian dropsocket++; 20811819Sjulian ipxp = (struct ipxpcb *)so->so_pcb; 20911819Sjulian ipxp->ipxp_laddr = si->si_dna; 21011819Sjulian cb = ipxtospxpcb(ipxp); 21111819Sjulian cb->s_mtu = ocb->s_mtu; /* preserve sockopts */ 21211819Sjulian cb->s_flags = ocb->s_flags; /* preserve sockopts */ 21311819Sjulian cb->s_flags2 = ocb->s_flags2; /* preserve sockopts */ 21411819Sjulian cb->s_state = TCPS_LISTEN; 21597658Stanimura } 21611819Sjulian 21711819Sjulian /* 21811819Sjulian * Packet received on connection. 21911819Sjulian * reset idle time and keep-alive timer; 22011819Sjulian */ 22111819Sjulian cb->s_idle = 0; 22211819Sjulian cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 22311819Sjulian 22411819Sjulian switch (cb->s_state) { 22511819Sjulian 22611819Sjulian case TCPS_LISTEN:{ 22728270Swollman struct sockaddr_ipx *sipx, ssipx; 22811819Sjulian struct ipx_addr laddr; 22911819Sjulian 23011819Sjulian /* 23111819Sjulian * If somebody here was carying on a conversation 23211819Sjulian * and went away, and his pen pal thinks he can 23311819Sjulian * still talk, we get the misdirected packet. 23411819Sjulian */ 23511819Sjulian if (spx_hardnosed && (si->si_did != 0 || si->si_seq != 0)) { 23611819Sjulian spx_istat.gonawy++; 23711819Sjulian goto dropwithreset; 23811819Sjulian } 23928270Swollman sipx = &ssipx; 24028270Swollman bzero(sipx, sizeof *sipx); 24111819Sjulian sipx->sipx_len = sizeof(*sipx); 24211819Sjulian sipx->sipx_family = AF_IPX; 24311819Sjulian sipx->sipx_addr = si->si_sna; 24411819Sjulian laddr = ipxp->ipxp_laddr; 24511819Sjulian if (ipx_nullhost(laddr)) 24611819Sjulian ipxp->ipxp_laddr = si->si_dna; 24790361Sjulian if (ipx_pcbconnect(ipxp, (struct sockaddr *)sipx, &thread0)) { 24811819Sjulian ipxp->ipxp_laddr = laddr; 24911819Sjulian spx_istat.noconn++; 25011819Sjulian goto drop; 25111819Sjulian } 25211819Sjulian spx_template(cb); 25311819Sjulian dropsocket = 0; /* committed to socket */ 25411819Sjulian cb->s_did = si->si_sid; 25511819Sjulian cb->s_rack = si->si_ack; 25611819Sjulian cb->s_ralo = si->si_alo; 25711819Sjulian#define THREEWAYSHAKE 25811819Sjulian#ifdef THREEWAYSHAKE 25911819Sjulian cb->s_state = TCPS_SYN_RECEIVED; 26011819Sjulian cb->s_force = 1 + SPXT_KEEP; 26111819Sjulian spxstat.spxs_accepts++; 26211819Sjulian cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 26311819Sjulian } 26411819Sjulian break; 26511819Sjulian /* 26611819Sjulian * This state means that we have heard a response 26711819Sjulian * to our acceptance of their connection 26811819Sjulian * It is probably logically unnecessary in this 26911819Sjulian * implementation. 27011819Sjulian */ 27111819Sjulian case TCPS_SYN_RECEIVED: { 27225652Sjhay if (si->si_did != cb->s_sid) { 27311819Sjulian spx_istat.wrncon++; 27411819Sjulian goto drop; 27511819Sjulian } 27611819Sjulian#endif 27711819Sjulian ipxp->ipxp_fport = si->si_sport; 27811819Sjulian cb->s_timer[SPXT_REXMT] = 0; 27911819Sjulian cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 28011819Sjulian soisconnected(so); 28111819Sjulian cb->s_state = TCPS_ESTABLISHED; 28211819Sjulian spxstat.spxs_accepts++; 28311819Sjulian } 28411819Sjulian break; 28511819Sjulian 28611819Sjulian /* 28711819Sjulian * This state means that we have gotten a response 28811819Sjulian * to our attempt to establish a connection. 28911819Sjulian * We fill in the data from the other side, 29011819Sjulian * telling us which port to respond to, instead of the well- 29111819Sjulian * known one we might have sent to in the first place. 29211819Sjulian * We also require that this is a response to our 29311819Sjulian * connection id. 29411819Sjulian */ 29511819Sjulian case TCPS_SYN_SENT: 29625652Sjhay if (si->si_did != cb->s_sid) { 29711819Sjulian spx_istat.notme++; 29811819Sjulian goto drop; 29911819Sjulian } 30011819Sjulian spxstat.spxs_connects++; 30111819Sjulian cb->s_did = si->si_sid; 30211819Sjulian cb->s_rack = si->si_ack; 30311819Sjulian cb->s_ralo = si->si_alo; 30411819Sjulian cb->s_dport = ipxp->ipxp_fport = si->si_sport; 30511819Sjulian cb->s_timer[SPXT_REXMT] = 0; 30611819Sjulian cb->s_flags |= SF_ACKNOW; 30711819Sjulian soisconnected(so); 30811819Sjulian cb->s_state = TCPS_ESTABLISHED; 30911819Sjulian /* Use roundtrip time of connection request for initial rtt */ 31011819Sjulian if (cb->s_rtt) { 31111819Sjulian cb->s_srtt = cb->s_rtt << 3; 31211819Sjulian cb->s_rttvar = cb->s_rtt << 1; 31311819Sjulian SPXT_RANGESET(cb->s_rxtcur, 31411819Sjulian ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1, 31511819Sjulian SPXTV_MIN, SPXTV_REXMTMAX); 31611819Sjulian cb->s_rtt = 0; 31711819Sjulian } 31811819Sjulian } 31997658Stanimura if (so->so_options & SO_DEBUG || traceallspxs) 32011819Sjulian spx_trace(SA_INPUT, (u_char)ostate, cb, &spx_savesi, 0); 32111819Sjulian 32225652Sjhay m->m_len -= sizeof(struct ipx); 32325652Sjhay m->m_pkthdr.len -= sizeof(struct ipx); 32425652Sjhay m->m_data += sizeof(struct ipx); 32511819Sjulian 32611819Sjulian if (spx_reass(cb, si)) { 32725652Sjhay m_freem(m); 32811819Sjulian } 32911819Sjulian if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT))) 330139579Srwatson spx_output(cb, NULL); 33111819Sjulian cb->s_flags &= ~(SF_WIN|SF_RXT); 33211819Sjulian return; 33311819Sjulian 33411819Sjuliandropwithreset: 335130822Srwatson if (dropsocket) { 336130822Srwatson struct socket *head; 337130822Srwatson ACCEPT_LOCK(); 338130822Srwatson KASSERT((so->so_qstate & SQ_INCOMP) != 0, 339130822Srwatson ("spx_input: nascent socket not SQ_INCOMP on soabort()")); 340130822Srwatson head = so->so_head; 341130822Srwatson TAILQ_REMOVE(&head->so_incomp, so, so_list); 342130822Srwatson head->so_incqlen--; 343130822Srwatson so->so_qstate &= ~SQ_INCOMP; 344130822Srwatson so->so_head = NULL; 345130822Srwatson ACCEPT_UNLOCK(); 34625652Sjhay soabort(so); 347130822Srwatson } 34811819Sjulian si->si_seq = ntohs(si->si_seq); 34911819Sjulian si->si_ack = ntohs(si->si_ack); 35011819Sjulian si->si_alo = ntohs(si->si_alo); 35125652Sjhay m_freem(dtom(si)); 35297658Stanimura if (cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || traceallspxs) 35311819Sjulian spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0); 35411819Sjulian return; 35511819Sjulian 35611819Sjuliandrop: 35711819Sjulianbad: 358139580Srwatson if (cb == NULL || cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || 35997658Stanimura traceallspxs) 36011819Sjulian spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0); 36111819Sjulian m_freem(m); 36211819Sjulian} 36311819Sjulian 36433181Seivindstatic int spxrexmtthresh = 3; 36511819Sjulian 36611819Sjulian/* 36711819Sjulian * This is structurally similar to the tcp reassembly routine 36811819Sjulian * but its function is somewhat different: It merely queues 36911819Sjulian * packets up, and suppresses duplicates. 37011819Sjulian */ 37125652Sjhaystatic int 37211819Sjulianspx_reass(cb, si) 37311819Sjulianregister struct spxpcb *cb; 37411819Sjulianregister struct spx *si; 37511819Sjulian{ 37611819Sjulian register struct spx_q *q; 37711819Sjulian register struct mbuf *m; 37811819Sjulian register struct socket *so = cb->s_ipxpcb->ipxp_socket; 37911819Sjulian char packetp = cb->s_flags & SF_HI; 38011819Sjulian int incr; 38111819Sjulian char wakeup = 0; 38211819Sjulian 38311819Sjulian if (si == SI(0)) 38411819Sjulian goto present; 38511819Sjulian /* 38611819Sjulian * Update our news from them. 38711819Sjulian */ 38811819Sjulian if (si->si_cc & SPX_SA) 38911819Sjulian cb->s_flags |= (spx_use_delack ? SF_DELACK : SF_ACKNOW); 39011819Sjulian if (SSEQ_GT(si->si_alo, cb->s_ralo)) 39111819Sjulian cb->s_flags |= SF_WIN; 39211819Sjulian if (SSEQ_LEQ(si->si_ack, cb->s_rack)) { 39311819Sjulian if ((si->si_cc & SPX_SP) && cb->s_rack != (cb->s_smax + 1)) { 39411819Sjulian spxstat.spxs_rcvdupack++; 39511819Sjulian /* 39611819Sjulian * If this is a completely duplicate ack 39711819Sjulian * and other conditions hold, we assume 39811819Sjulian * a packet has been dropped and retransmit 39911819Sjulian * it exactly as in tcp_input(). 40011819Sjulian */ 40111819Sjulian if (si->si_ack != cb->s_rack || 40211819Sjulian si->si_alo != cb->s_ralo) 40311819Sjulian cb->s_dupacks = 0; 40411819Sjulian else if (++cb->s_dupacks == spxrexmtthresh) { 40511819Sjulian u_short onxt = cb->s_snxt; 40611819Sjulian int cwnd = cb->s_cwnd; 40711819Sjulian 40811819Sjulian cb->s_snxt = si->si_ack; 40911819Sjulian cb->s_cwnd = CUNIT; 41011819Sjulian cb->s_force = 1 + SPXT_REXMT; 411139579Srwatson spx_output(cb, NULL); 41211819Sjulian cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 41311819Sjulian cb->s_rtt = 0; 41411819Sjulian if (cwnd >= 4 * CUNIT) 41511819Sjulian cb->s_cwnd = cwnd / 2; 41611819Sjulian if (SSEQ_GT(onxt, cb->s_snxt)) 41711819Sjulian cb->s_snxt = onxt; 41811819Sjulian return (1); 41911819Sjulian } 42011819Sjulian } else 42111819Sjulian cb->s_dupacks = 0; 42211819Sjulian goto update_window; 42311819Sjulian } 42411819Sjulian cb->s_dupacks = 0; 42511819Sjulian /* 42611819Sjulian * If our correspondent acknowledges data we haven't sent 42711819Sjulian * TCP would drop the packet after acking. We'll be a little 42811819Sjulian * more permissive 42911819Sjulian */ 43011819Sjulian if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) { 43111819Sjulian spxstat.spxs_rcvacktoomuch++; 43211819Sjulian si->si_ack = cb->s_smax + 1; 43311819Sjulian } 43411819Sjulian spxstat.spxs_rcvackpack++; 43511819Sjulian /* 43611819Sjulian * If transmit timer is running and timed sequence 43711819Sjulian * number was acked, update smoothed round trip time. 43811819Sjulian * See discussion of algorithm in tcp_input.c 43911819Sjulian */ 44011819Sjulian if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) { 44111819Sjulian spxstat.spxs_rttupdated++; 44211819Sjulian if (cb->s_srtt != 0) { 44311819Sjulian register short delta; 44411819Sjulian delta = cb->s_rtt - (cb->s_srtt >> 3); 44511819Sjulian if ((cb->s_srtt += delta) <= 0) 44611819Sjulian cb->s_srtt = 1; 44711819Sjulian if (delta < 0) 44811819Sjulian delta = -delta; 44911819Sjulian delta -= (cb->s_rttvar >> 2); 45011819Sjulian if ((cb->s_rttvar += delta) <= 0) 45111819Sjulian cb->s_rttvar = 1; 45211819Sjulian } else { 45311819Sjulian /* 45411819Sjulian * No rtt measurement yet 45511819Sjulian */ 45611819Sjulian cb->s_srtt = cb->s_rtt << 3; 45711819Sjulian cb->s_rttvar = cb->s_rtt << 1; 45811819Sjulian } 45911819Sjulian cb->s_rtt = 0; 46011819Sjulian cb->s_rxtshift = 0; 46111819Sjulian SPXT_RANGESET(cb->s_rxtcur, 46211819Sjulian ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1, 46311819Sjulian SPXTV_MIN, SPXTV_REXMTMAX); 46411819Sjulian } 46511819Sjulian /* 46611819Sjulian * If all outstanding data is acked, stop retransmit 46711819Sjulian * timer and remember to restart (more output or persist). 46811819Sjulian * If there is more data to be acked, restart retransmit 46911819Sjulian * timer, using current (possibly backed-off) value; 47011819Sjulian */ 47111819Sjulian if (si->si_ack == cb->s_smax + 1) { 47211819Sjulian cb->s_timer[SPXT_REXMT] = 0; 47311819Sjulian cb->s_flags |= SF_RXT; 47411819Sjulian } else if (cb->s_timer[SPXT_PERSIST] == 0) 47511819Sjulian cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 47611819Sjulian /* 47711819Sjulian * When new data is acked, open the congestion window. 47811819Sjulian * If the window gives us less than ssthresh packets 47911819Sjulian * in flight, open exponentially (maxseg at a time). 48011819Sjulian * Otherwise open linearly (maxseg^2 / cwnd at a time). 48111819Sjulian */ 48211819Sjulian incr = CUNIT; 48311819Sjulian if (cb->s_cwnd > cb->s_ssthresh) 48411819Sjulian incr = max(incr * incr / cb->s_cwnd, 1); 48511819Sjulian cb->s_cwnd = min(cb->s_cwnd + incr, cb->s_cwmx); 48611819Sjulian /* 48711819Sjulian * Trim Acked data from output queue. 48811819Sjulian */ 489139589Srwatson SOCKBUF_LOCK(&so->so_snd); 49011819Sjulian while ((m = so->so_snd.sb_mb) != NULL) { 49111819Sjulian if (SSEQ_LT((mtod(m, struct spx *))->si_seq, si->si_ack)) 492139589Srwatson sbdroprecord_locked(&so->so_snd); 49311819Sjulian else 49411819Sjulian break; 49511819Sjulian } 496139589Srwatson sowwakeup_locked(so); 49711819Sjulian cb->s_rack = si->si_ack; 49811819Sjulianupdate_window: 49911819Sjulian if (SSEQ_LT(cb->s_snxt, cb->s_rack)) 50011819Sjulian cb->s_snxt = cb->s_rack; 50143311Sdillon if (SSEQ_LT(cb->s_swl1, si->si_seq) || ((cb->s_swl1 == si->si_seq && 50243311Sdillon (SSEQ_LT(cb->s_swl2, si->si_ack))) || 50343305Sdillon (cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo)))) { 50411819Sjulian /* keep track of pure window updates */ 50511819Sjulian if ((si->si_cc & SPX_SP) && cb->s_swl2 == si->si_ack 50611819Sjulian && SSEQ_LT(cb->s_ralo, si->si_alo)) { 50711819Sjulian spxstat.spxs_rcvwinupd++; 50811819Sjulian spxstat.spxs_rcvdupack--; 50911819Sjulian } 51011819Sjulian cb->s_ralo = si->si_alo; 51111819Sjulian cb->s_swl1 = si->si_seq; 51211819Sjulian cb->s_swl2 = si->si_ack; 51311819Sjulian cb->s_swnd = (1 + si->si_alo - si->si_ack); 51411819Sjulian if (cb->s_swnd > cb->s_smxw) 51511819Sjulian cb->s_smxw = cb->s_swnd; 51611819Sjulian cb->s_flags |= SF_WIN; 51711819Sjulian } 51811819Sjulian /* 51911819Sjulian * If this packet number is higher than that which 52011819Sjulian * we have allocated refuse it, unless urgent 52111819Sjulian */ 52211819Sjulian if (SSEQ_GT(si->si_seq, cb->s_alo)) { 52311819Sjulian if (si->si_cc & SPX_SP) { 52411819Sjulian spxstat.spxs_rcvwinprobe++; 52511819Sjulian return (1); 52611819Sjulian } else 52711819Sjulian spxstat.spxs_rcvpackafterwin++; 52811819Sjulian if (si->si_cc & SPX_OB) { 52911819Sjulian if (SSEQ_GT(si->si_seq, cb->s_alo + 60)) { 53025652Sjhay m_freem(dtom(si)); 53111819Sjulian return (0); 53211819Sjulian } /* else queue this packet; */ 53311819Sjulian } else { 534139579Srwatson#ifdef BROKEN 535139579Srwatson /* 536139579Srwatson * XXXRW: This is broken on at least one count: 537139579Srwatson * spx_close() will free the ipxp and related parts, 538139579Srwatson * which are then touched by spx_input() after the 539139579Srwatson * return from spx_reass(). 540139579Srwatson */ 54111819Sjulian /*register struct socket *so = cb->s_ipxpcb->ipxp_socket; 54211819Sjulian if (so->so_state && SS_NOFDREF) { 54325652Sjhay spx_close(cb); 54497658Stanimura } else 54597658Stanimura would crash system*/ 546139579Srwatson#endif 54711819Sjulian spx_istat.notyet++; 54825652Sjhay m_freem(dtom(si)); 54911819Sjulian return (0); 55011819Sjulian } 55111819Sjulian } 55211819Sjulian /* 55311819Sjulian * If this is a system packet, we don't need to 55411819Sjulian * queue it up, and won't update acknowledge # 55511819Sjulian */ 55611819Sjulian if (si->si_cc & SPX_SP) { 55711819Sjulian return (1); 55811819Sjulian } 55911819Sjulian /* 56011819Sjulian * We have already seen this packet, so drop. 56111819Sjulian */ 56211819Sjulian if (SSEQ_LT(si->si_seq, cb->s_ack)) { 56311819Sjulian spx_istat.bdreas++; 56411819Sjulian spxstat.spxs_rcvduppack++; 56511819Sjulian if (si->si_seq == cb->s_ack - 1) 56611819Sjulian spx_istat.lstdup++; 56711819Sjulian return (1); 56811819Sjulian } 56911819Sjulian /* 57011819Sjulian * Loop through all packets queued up to insert in 57111819Sjulian * appropriate sequence. 57211819Sjulian */ 57325652Sjhay for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) { 57411819Sjulian if (si->si_seq == SI(q)->si_seq) { 57511819Sjulian spxstat.spxs_rcvduppack++; 57611819Sjulian return (1); 57711819Sjulian } 57811819Sjulian if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) { 57911819Sjulian spxstat.spxs_rcvoopack++; 58011819Sjulian break; 58111819Sjulian } 58211819Sjulian } 58311819Sjulian insque(si, q->si_prev); 58411819Sjulian /* 58511819Sjulian * If this packet is urgent, inform process 58611819Sjulian */ 58711819Sjulian if (si->si_cc & SPX_OB) { 58811819Sjulian cb->s_iobc = ((char *)si)[1 + sizeof(*si)]; 58911819Sjulian sohasoutofband(so); 59011819Sjulian cb->s_oobflags |= SF_IOOB; 59111819Sjulian } 59211819Sjulianpresent: 59311819Sjulian#define SPINC sizeof(struct spxhdr) 59411819Sjulian /* 59511819Sjulian * Loop through all packets queued up to update acknowledge 59611819Sjulian * number, and present all acknowledged data to user; 59711819Sjulian * If in packet interface mode, show packet headers. 59811819Sjulian */ 59925652Sjhay for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) { 60011819Sjulian if (SI(q)->si_seq == cb->s_ack) { 60111819Sjulian cb->s_ack++; 60211819Sjulian m = dtom(q); 60311819Sjulian if (SI(q)->si_cc & SPX_OB) { 60411819Sjulian cb->s_oobflags &= ~SF_IOOB; 605131031Srwatson SOCKBUF_LOCK(&so->so_rcv); 60611819Sjulian if (so->so_rcv.sb_cc) 60711819Sjulian so->so_oobmark = so->so_rcv.sb_cc; 608131031Srwatson else 609130480Srwatson so->so_rcv.sb_state |= SBS_RCVATMARK; 610131031Srwatson SOCKBUF_UNLOCK(&so->so_rcv); 61111819Sjulian } 61211819Sjulian q = q->si_prev; 61311819Sjulian remque(q->si_next); 61411819Sjulian wakeup = 1; 61511819Sjulian spxstat.spxs_rcvpack++; 61611819Sjulian#ifdef SF_NEWCALL 61711819Sjulian if (cb->s_flags2 & SF_NEWCALL) { 61811819Sjulian struct spxhdr *sp = mtod(m, struct spxhdr *); 61911819Sjulian u_char dt = sp->spx_dt; 62011819Sjulian spx_newchecks[4]++; 62111819Sjulian if (dt != cb->s_rhdr.spx_dt) { 62211819Sjulian struct mbuf *mm = 623111119Simp m_getclr(M_DONTWAIT, MT_CONTROL); 62411819Sjulian spx_newchecks[0]++; 62511819Sjulian if (mm != NULL) { 62611819Sjulian u_short *s = 62711819Sjulian mtod(mm, u_short *); 62811819Sjulian cb->s_rhdr.spx_dt = dt; 62911819Sjulian mm->m_len = 5; /*XXX*/ 63011819Sjulian s[0] = 5; 63111819Sjulian s[1] = 1; 63211819Sjulian *(u_char *)(&s[2]) = dt; 63311819Sjulian sbappend(&so->so_rcv, mm); 63411819Sjulian } 63511819Sjulian } 63611819Sjulian if (sp->spx_cc & SPX_OB) { 63711819Sjulian MCHTYPE(m, MT_OOBDATA); 63811819Sjulian spx_newchecks[1]++; 639131031Srwatson SOCKBUF_LOCK(&so->so_rcv); 64011819Sjulian so->so_oobmark = 0; 641130480Srwatson so->so_rcv.sb_state &= ~SBS_RCVATMARK; 642130513Srwatson SOCKBUF_UNLOCK(&so->so_rcv); 64311819Sjulian } 64411819Sjulian if (packetp == 0) { 64511819Sjulian m->m_data += SPINC; 64611819Sjulian m->m_len -= SPINC; 64711819Sjulian m->m_pkthdr.len -= SPINC; 64811819Sjulian } 64911819Sjulian if ((sp->spx_cc & SPX_EM) || packetp) { 65011819Sjulian sbappendrecord(&so->so_rcv, m); 65111819Sjulian spx_newchecks[9]++; 65211819Sjulian } else 65311819Sjulian sbappend(&so->so_rcv, m); 65411819Sjulian } else 65511819Sjulian#endif 65611819Sjulian if (packetp) { 65711819Sjulian sbappendrecord(&so->so_rcv, m); 65811819Sjulian } else { 65911819Sjulian cb->s_rhdr = *mtod(m, struct spxhdr *); 66011819Sjulian m->m_data += SPINC; 66111819Sjulian m->m_len -= SPINC; 66211819Sjulian m->m_pkthdr.len -= SPINC; 66311819Sjulian sbappend(&so->so_rcv, m); 66411819Sjulian } 66511819Sjulian } else 66611819Sjulian break; 66711819Sjulian } 66897658Stanimura if (wakeup) 66925652Sjhay sorwakeup(so); 67011819Sjulian return (0); 67111819Sjulian} 67211819Sjulian 67311819Sjulianvoid 67412881Sbdespx_ctlinput(cmd, arg_as_sa, dummy) 67511819Sjulian int cmd; 67612881Sbde struct sockaddr *arg_as_sa; /* XXX should be swapped with dummy */ 67712881Sbde void *dummy; 67811819Sjulian{ 67912881Sbde caddr_t arg = (/* XXX */ caddr_t)arg_as_sa; 68011819Sjulian struct ipx_addr *na; 68125652Sjhay struct sockaddr_ipx *sipx; 68211819Sjulian 683119995Sru if (cmd < 0 || cmd >= PRC_NCMDS) 68411819Sjulian return; 68525652Sjhay 68611819Sjulian switch (cmd) { 68711819Sjulian 68811819Sjulian case PRC_ROUTEDEAD: 68911819Sjulian return; 69011819Sjulian 69111819Sjulian case PRC_IFDOWN: 69211819Sjulian case PRC_HOSTDEAD: 69311819Sjulian case PRC_HOSTUNREACH: 69411819Sjulian sipx = (struct sockaddr_ipx *)arg; 69511819Sjulian if (sipx->sipx_family != AF_IPX) 69611819Sjulian return; 69711819Sjulian na = &sipx->sipx_addr; 69811819Sjulian break; 69911819Sjulian 70011819Sjulian default: 70111819Sjulian break; 70211819Sjulian } 70311819Sjulian} 70411819Sjulian 70525652Sjhaystatic int 70611819Sjulianspx_output(cb, m0) 70711819Sjulian register struct spxpcb *cb; 70811819Sjulian struct mbuf *m0; 70911819Sjulian{ 71011819Sjulian struct socket *so = cb->s_ipxpcb->ipxp_socket; 71111819Sjulian register struct mbuf *m; 712139579Srwatson register struct spx *si = NULL; 71311819Sjulian register struct sockbuf *sb = &so->so_snd; 71411819Sjulian int len = 0, win, rcv_win; 71511819Sjulian short span, off, recordp = 0; 71611819Sjulian u_short alo; 71711819Sjulian int error = 0, sendalot; 71811819Sjulian#ifdef notdef 71911819Sjulian int idle; 72011819Sjulian#endif 72111819Sjulian struct mbuf *mprev; 72211819Sjulian 72325652Sjhay if (m0 != NULL) { 72411819Sjulian int mtu = cb->s_mtu; 72511819Sjulian int datalen; 72611819Sjulian /* 72711819Sjulian * Make sure that packet isn't too big. 72811819Sjulian */ 72925652Sjhay for (m = m0; m != NULL; m = m->m_next) { 73011819Sjulian mprev = m; 73111819Sjulian len += m->m_len; 73211819Sjulian if (m->m_flags & M_EOR) 73311819Sjulian recordp = 1; 73411819Sjulian } 73511819Sjulian datalen = (cb->s_flags & SF_HO) ? 73625652Sjhay len - sizeof(struct spxhdr) : len; 73711819Sjulian if (datalen > mtu) { 73811819Sjulian if (cb->s_flags & SF_PI) { 73911819Sjulian m_freem(m0); 74011819Sjulian return (EMSGSIZE); 74111819Sjulian } else { 74211819Sjulian int oldEM = cb->s_cc & SPX_EM; 74311819Sjulian 74411819Sjulian cb->s_cc &= ~SPX_EM; 74511819Sjulian while (len > mtu) { 74611819Sjulian /* 74711819Sjulian * Here we are only being called 74811819Sjulian * from usrreq(), so it is OK to 74911819Sjulian * block. 75011819Sjulian */ 751111119Simp m = m_copym(m0, 0, mtu, M_TRYWAIT); 75211819Sjulian if (cb->s_flags & SF_NEWCALL) { 75311819Sjulian struct mbuf *mm = m; 75411819Sjulian spx_newchecks[7]++; 75525652Sjhay while (mm != NULL) { 75611819Sjulian mm->m_flags &= ~M_EOR; 75711819Sjulian mm = mm->m_next; 75811819Sjulian } 75911819Sjulian } 76011819Sjulian error = spx_output(cb, m); 76111819Sjulian if (error) { 76211819Sjulian cb->s_cc |= oldEM; 76311819Sjulian m_freem(m0); 76425652Sjhay return (error); 76511819Sjulian } 76611819Sjulian m_adj(m0, mtu); 76711819Sjulian len -= mtu; 76811819Sjulian } 76911819Sjulian cb->s_cc |= oldEM; 77011819Sjulian } 77111819Sjulian } 77211819Sjulian /* 77311819Sjulian * Force length even, by adding a "garbage byte" if 77411819Sjulian * necessary. 77511819Sjulian */ 77611819Sjulian if (len & 1) { 77711819Sjulian m = mprev; 77811819Sjulian if (M_TRAILINGSPACE(m) >= 1) 77911819Sjulian m->m_len++; 78011819Sjulian else { 781111119Simp struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA); 78211819Sjulian 78325652Sjhay if (m1 == NULL) { 78411819Sjulian m_freem(m0); 78511819Sjulian return (ENOBUFS); 78611819Sjulian } 78711819Sjulian m1->m_len = 1; 78811819Sjulian *(mtod(m1, u_char *)) = 0; 78911819Sjulian m->m_next = m1; 79011819Sjulian } 79111819Sjulian } 792111119Simp m = m_gethdr(M_DONTWAIT, MT_HEADER); 79325652Sjhay if (m == NULL) { 79411819Sjulian m_freem(m0); 79511819Sjulian return (ENOBUFS); 79611819Sjulian } 79711819Sjulian /* 79811819Sjulian * Fill in mbuf with extended SP header 79911819Sjulian * and addresses and length put into network format. 80011819Sjulian */ 80125652Sjhay MH_ALIGN(m, sizeof(struct spx)); 80225652Sjhay m->m_len = sizeof(struct spx); 80311819Sjulian m->m_next = m0; 80411819Sjulian si = mtod(m, struct spx *); 80511819Sjulian si->si_i = *cb->s_ipx; 80611819Sjulian si->si_s = cb->s_shdr; 80711819Sjulian if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) { 80811819Sjulian register struct spxhdr *sh; 80925652Sjhay if (m0->m_len < sizeof(*sh)) { 81011819Sjulian if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) { 81125652Sjhay m_free(m); 81211819Sjulian m_freem(m0); 81311819Sjulian return (EINVAL); 81411819Sjulian } 81511819Sjulian m->m_next = m0; 81611819Sjulian } 81711819Sjulian sh = mtod(m0, struct spxhdr *); 81811819Sjulian si->si_dt = sh->spx_dt; 81911819Sjulian si->si_cc |= sh->spx_cc & SPX_EM; 82025652Sjhay m0->m_len -= sizeof(*sh); 82125652Sjhay m0->m_data += sizeof(*sh); 82225652Sjhay len -= sizeof(*sh); 82311819Sjulian } 82411819Sjulian len += sizeof(*si); 82511819Sjulian if ((cb->s_flags2 & SF_NEWCALL) && recordp) { 82625652Sjhay si->si_cc |= SPX_EM; 82711819Sjulian spx_newchecks[8]++; 82811819Sjulian } 82911819Sjulian if (cb->s_oobflags & SF_SOOB) { 83011819Sjulian /* 83111819Sjulian * Per jqj@cornell: 83211819Sjulian * make sure OB packets convey exactly 1 byte. 83311819Sjulian * If the packet is 1 byte or larger, we 83411819Sjulian * have already guaranted there to be at least 83511819Sjulian * one garbage byte for the checksum, and 83611819Sjulian * extra bytes shouldn't hurt! 83711819Sjulian */ 83811819Sjulian if (len > sizeof(*si)) { 83911819Sjulian si->si_cc |= SPX_OB; 84011819Sjulian len = (1 + sizeof(*si)); 84111819Sjulian } 84211819Sjulian } 84311819Sjulian si->si_len = htons((u_short)len); 84411819Sjulian m->m_pkthdr.len = ((len - 1) | 1) + 1; 84511819Sjulian /* 84611819Sjulian * queue stuff up for output 84711819Sjulian */ 84811819Sjulian sbappendrecord(sb, m); 84911819Sjulian cb->s_seq++; 85011819Sjulian } 85111819Sjulian#ifdef notdef 85211819Sjulian idle = (cb->s_smax == (cb->s_rack - 1)); 85311819Sjulian#endif 85411819Sjulianagain: 85511819Sjulian sendalot = 0; 85611819Sjulian off = cb->s_snxt - cb->s_rack; 85725652Sjhay win = min(cb->s_swnd, (cb->s_cwnd / CUNIT)); 85811819Sjulian 85911819Sjulian /* 86011819Sjulian * If in persist timeout with window of 0, send a probe. 86111819Sjulian * Otherwise, if window is small but nonzero 86211819Sjulian * and timer expired, send what we can and go into 86311819Sjulian * transmit state. 86411819Sjulian */ 86511819Sjulian if (cb->s_force == 1 + SPXT_PERSIST) { 86611819Sjulian if (win != 0) { 86711819Sjulian cb->s_timer[SPXT_PERSIST] = 0; 86811819Sjulian cb->s_rxtshift = 0; 86911819Sjulian } 87011819Sjulian } 87111819Sjulian span = cb->s_seq - cb->s_rack; 87211819Sjulian len = min(span, win) - off; 87311819Sjulian 87411819Sjulian if (len < 0) { 87511819Sjulian /* 87611819Sjulian * Window shrank after we went into it. 87711819Sjulian * If window shrank to 0, cancel pending 87811819Sjulian * restransmission and pull s_snxt back 87911819Sjulian * to (closed) window. We will enter persist 88011819Sjulian * state below. If the widndow didn't close completely, 88111819Sjulian * just wait for an ACK. 88211819Sjulian */ 88311819Sjulian len = 0; 88411819Sjulian if (win == 0) { 88511819Sjulian cb->s_timer[SPXT_REXMT] = 0; 88611819Sjulian cb->s_snxt = cb->s_rack; 88711819Sjulian } 88811819Sjulian } 88911819Sjulian if (len > 1) 89011819Sjulian sendalot = 1; 89111819Sjulian rcv_win = sbspace(&so->so_rcv); 89211819Sjulian 89311819Sjulian /* 89411819Sjulian * Send if we owe peer an ACK. 89511819Sjulian */ 89611819Sjulian if (cb->s_oobflags & SF_SOOB) { 89711819Sjulian /* 89811819Sjulian * must transmit this out of band packet 89911819Sjulian */ 90011819Sjulian cb->s_oobflags &= ~ SF_SOOB; 90111819Sjulian sendalot = 1; 90211819Sjulian spxstat.spxs_sndurg++; 90311819Sjulian goto found; 90411819Sjulian } 90511819Sjulian if (cb->s_flags & SF_ACKNOW) 90611819Sjulian goto send; 90711819Sjulian if (cb->s_state < TCPS_ESTABLISHED) 90811819Sjulian goto send; 90911819Sjulian /* 91011819Sjulian * Silly window can't happen in spx. 91111819Sjulian * Code from tcp deleted. 91211819Sjulian */ 91311819Sjulian if (len) 91411819Sjulian goto send; 91511819Sjulian /* 91611819Sjulian * Compare available window to amount of window 91711819Sjulian * known to peer (as advertised window less 91811819Sjulian * next expected input.) If the difference is at least two 91911819Sjulian * packets or at least 35% of the mximum possible window, 92011819Sjulian * then want to send a window update to peer. 92111819Sjulian */ 92211819Sjulian if (rcv_win > 0) { 92311819Sjulian u_short delta = 1 + cb->s_alo - cb->s_ack; 92411819Sjulian int adv = rcv_win - (delta * cb->s_mtu); 925139584Srwatson 92611819Sjulian if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) || 92711819Sjulian (100 * adv / so->so_rcv.sb_hiwat >= 35)) { 92811819Sjulian spxstat.spxs_sndwinup++; 92911819Sjulian cb->s_flags |= SF_ACKNOW; 93011819Sjulian goto send; 93111819Sjulian } 93211819Sjulian 93311819Sjulian } 93411819Sjulian /* 93511819Sjulian * Many comments from tcp_output.c are appropriate here 93611819Sjulian * including . . . 93711819Sjulian * If send window is too small, there is data to transmit, and no 93811819Sjulian * retransmit or persist is pending, then go to persist state. 93911819Sjulian * If nothing happens soon, send when timer expires: 94011819Sjulian * if window is nonzero, transmit what we can, 94111819Sjulian * otherwise send a probe. 94211819Sjulian */ 94311819Sjulian if (so->so_snd.sb_cc && cb->s_timer[SPXT_REXMT] == 0 && 94411819Sjulian cb->s_timer[SPXT_PERSIST] == 0) { 94511819Sjulian cb->s_rxtshift = 0; 94611819Sjulian spx_setpersist(cb); 94711819Sjulian } 94811819Sjulian /* 94911819Sjulian * No reason to send a packet, just return. 95011819Sjulian */ 95111819Sjulian cb->s_outx = 1; 95211819Sjulian return (0); 95311819Sjulian 95411819Sjuliansend: 95511819Sjulian /* 95611819Sjulian * Find requested packet. 95711819Sjulian */ 95811819Sjulian si = 0; 95911819Sjulian if (len > 0) { 96011819Sjulian cb->s_want = cb->s_snxt; 96125652Sjhay for (m = sb->sb_mb; m != NULL; m = m->m_act) { 96211819Sjulian si = mtod(m, struct spx *); 96311819Sjulian if (SSEQ_LEQ(cb->s_snxt, si->si_seq)) 96411819Sjulian break; 96511819Sjulian } 96611819Sjulian found: 96725652Sjhay if (si != NULL) { 96811819Sjulian if (si->si_seq == cb->s_snxt) 96911819Sjulian cb->s_snxt++; 97011819Sjulian else 97111819Sjulian spxstat.spxs_sndvoid++, si = 0; 97211819Sjulian } 97311819Sjulian } 97411819Sjulian /* 97511819Sjulian * update window 97611819Sjulian */ 97711819Sjulian if (rcv_win < 0) 97811819Sjulian rcv_win = 0; 97911819Sjulian alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu)); 980139584Srwatson if (SSEQ_LT(alo, cb->s_alo)) 98111819Sjulian alo = cb->s_alo; 98211819Sjulian 98325652Sjhay if (si != NULL) { 98411819Sjulian /* 98511819Sjulian * must make a copy of this packet for 98611819Sjulian * ipx_output to monkey with 98711819Sjulian */ 98811819Sjulian m = m_copy(dtom(si), 0, (int)M_COPYALL); 98911819Sjulian if (m == NULL) { 99011819Sjulian return (ENOBUFS); 99111819Sjulian } 99211819Sjulian si = mtod(m, struct spx *); 99311819Sjulian if (SSEQ_LT(si->si_seq, cb->s_smax)) 99411819Sjulian spxstat.spxs_sndrexmitpack++; 99511819Sjulian else 99611819Sjulian spxstat.spxs_sndpack++; 99711819Sjulian } else if (cb->s_force || cb->s_flags & SF_ACKNOW) { 99811819Sjulian /* 99911819Sjulian * Must send an acknowledgement or a probe 100011819Sjulian */ 100111819Sjulian if (cb->s_force) 100211819Sjulian spxstat.spxs_sndprobe++; 100311819Sjulian if (cb->s_flags & SF_ACKNOW) 100411819Sjulian spxstat.spxs_sndacks++; 1005111119Simp m = m_gethdr(M_DONTWAIT, MT_HEADER); 100625652Sjhay if (m == NULL) 100711819Sjulian return (ENOBUFS); 100811819Sjulian /* 100911819Sjulian * Fill in mbuf with extended SP header 101011819Sjulian * and addresses and length put into network format. 101111819Sjulian */ 101225652Sjhay MH_ALIGN(m, sizeof(struct spx)); 101325652Sjhay m->m_len = sizeof(*si); 101425652Sjhay m->m_pkthdr.len = sizeof(*si); 101511819Sjulian si = mtod(m, struct spx *); 101611819Sjulian si->si_i = *cb->s_ipx; 101711819Sjulian si->si_s = cb->s_shdr; 101811819Sjulian si->si_seq = cb->s_smax + 1; 101925652Sjhay si->si_len = htons(sizeof(*si)); 102011819Sjulian si->si_cc |= SPX_SP; 102111819Sjulian } else { 102211819Sjulian cb->s_outx = 3; 102397658Stanimura if (so->so_options & SO_DEBUG || traceallspxs) 102411819Sjulian spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0); 102511819Sjulian return (0); 102611819Sjulian } 102711819Sjulian /* 102811819Sjulian * Stuff checksum and output datagram. 102911819Sjulian */ 103011819Sjulian if ((si->si_cc & SPX_SP) == 0) { 103111819Sjulian if (cb->s_force != (1 + SPXT_PERSIST) || 103211819Sjulian cb->s_timer[SPXT_PERSIST] == 0) { 103311819Sjulian /* 1034139584Srwatson * If this is a new packet and we are not currently 103511819Sjulian * timing anything, time this one. 103611819Sjulian */ 103711819Sjulian if (SSEQ_LT(cb->s_smax, si->si_seq)) { 103811819Sjulian cb->s_smax = si->si_seq; 103911819Sjulian if (cb->s_rtt == 0) { 104011819Sjulian spxstat.spxs_segstimed++; 104111819Sjulian cb->s_rtseq = si->si_seq; 104211819Sjulian cb->s_rtt = 1; 104311819Sjulian } 104411819Sjulian } 104511819Sjulian /* 104611819Sjulian * Set rexmt timer if not currently set, 104711819Sjulian * Initial value for retransmit timer is smoothed 104811819Sjulian * round-trip time + 2 * round-trip time variance. 104911819Sjulian * Initialize shift counter which is used for backoff 105011819Sjulian * of retransmit time. 105111819Sjulian */ 105211819Sjulian if (cb->s_timer[SPXT_REXMT] == 0 && 105311819Sjulian cb->s_snxt != cb->s_rack) { 105411819Sjulian cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 105511819Sjulian if (cb->s_timer[SPXT_PERSIST]) { 105611819Sjulian cb->s_timer[SPXT_PERSIST] = 0; 105711819Sjulian cb->s_rxtshift = 0; 105811819Sjulian } 105911819Sjulian } 106011819Sjulian } else if (SSEQ_LT(cb->s_smax, si->si_seq)) { 106111819Sjulian cb->s_smax = si->si_seq; 106211819Sjulian } 106311819Sjulian } else if (cb->s_state < TCPS_ESTABLISHED) { 106411819Sjulian if (cb->s_rtt == 0) 106511819Sjulian cb->s_rtt = 1; /* Time initial handshake */ 106611819Sjulian if (cb->s_timer[SPXT_REXMT] == 0) 106711819Sjulian cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 106811819Sjulian } 106911819Sjulian { 107011819Sjulian /* 107111819Sjulian * Do not request acks when we ack their data packets or 107211819Sjulian * when we do a gratuitous window update. 107311819Sjulian */ 107411819Sjulian if (((si->si_cc & SPX_SP) == 0) || cb->s_force) 107511819Sjulian si->si_cc |= SPX_SA; 107611819Sjulian si->si_seq = htons(si->si_seq); 107711819Sjulian si->si_alo = htons(alo); 107811819Sjulian si->si_ack = htons(cb->s_ack); 107911819Sjulian 108011819Sjulian if (ipxcksum) { 108150519Sjhay si->si_sum = ipx_cksum(m, ntohs(si->si_len)); 108211819Sjulian } else 108311819Sjulian si->si_sum = 0xffff; 108411819Sjulian 108511819Sjulian cb->s_outx = 4; 108697658Stanimura if (so->so_options & SO_DEBUG || traceallspxs) 108711819Sjulian spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0); 108811819Sjulian 108997658Stanimura if (so->so_options & SO_DONTROUTE) 1090139579Srwatson error = ipx_outputfl(m, NULL, IPX_ROUTETOIF); 109197658Stanimura else 109211819Sjulian error = ipx_outputfl(m, &cb->s_ipxpcb->ipxp_route, 0); 109311819Sjulian } 109411819Sjulian if (error) { 109511819Sjulian return (error); 109611819Sjulian } 109711819Sjulian spxstat.spxs_sndtotal++; 109811819Sjulian /* 109911819Sjulian * Data sent (as far as we can tell). 110011819Sjulian * If this advertises a larger window than any other segment, 110111819Sjulian * then remember the size of the advertized window. 110211819Sjulian * Any pending ACK has now been sent. 110311819Sjulian */ 110411819Sjulian cb->s_force = 0; 110511819Sjulian cb->s_flags &= ~(SF_ACKNOW|SF_DELACK); 110611819Sjulian if (SSEQ_GT(alo, cb->s_alo)) 110711819Sjulian cb->s_alo = alo; 110811819Sjulian if (sendalot) 110911819Sjulian goto again; 111011819Sjulian cb->s_outx = 5; 111111819Sjulian return (0); 111211819Sjulian} 111311819Sjulian 111433181Seivindstatic int spx_do_persist_panics = 0; 111511819Sjulian 111625652Sjhaystatic void 111711819Sjulianspx_setpersist(cb) 111811819Sjulian register struct spxpcb *cb; 111911819Sjulian{ 112035599Sbde register int t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1; 112111819Sjulian 112211819Sjulian if (cb->s_timer[SPXT_REXMT] && spx_do_persist_panics) 112311819Sjulian panic("spx_output REXMT"); 112411819Sjulian /* 112511819Sjulian * Start/restart persistance timer. 112611819Sjulian */ 112711819Sjulian SPXT_RANGESET(cb->s_timer[SPXT_PERSIST], 112811819Sjulian t*spx_backoff[cb->s_rxtshift], 112911819Sjulian SPXTV_PERSMIN, SPXTV_PERSMAX); 113011819Sjulian if (cb->s_rxtshift < SPX_MAXRXTSHIFT) 113111819Sjulian cb->s_rxtshift++; 113211819Sjulian} 113325652Sjhay 113411819Sjulianint 113538482Swollmanspx_ctloutput(so, sopt) 113611819Sjulian struct socket *so; 113738482Swollman struct sockopt *sopt; 113811819Sjulian{ 113911819Sjulian struct ipxpcb *ipxp = sotoipxpcb(so); 114011819Sjulian register struct spxpcb *cb; 114138482Swollman int mask, error; 114238482Swollman short soptval; 114338482Swollman u_short usoptval; 114438482Swollman int optval; 114511819Sjulian 114638482Swollman error = 0; 114738482Swollman 114838482Swollman if (sopt->sopt_level != IPXPROTO_SPX) { 114911819Sjulian /* This will have to be changed when we do more general 115011819Sjulian stacking of protocols */ 115138482Swollman return (ipx_ctloutput(so, sopt)); 115211819Sjulian } 115338482Swollman if (ipxp == NULL) 115438482Swollman return (EINVAL); 115538482Swollman else 115611819Sjulian cb = ipxtospxpcb(ipxp); 115711819Sjulian 115838482Swollman switch (sopt->sopt_dir) { 115938482Swollman case SOPT_GET: 116038482Swollman switch (sopt->sopt_name) { 116111819Sjulian case SO_HEADERS_ON_INPUT: 116211819Sjulian mask = SF_HI; 116311819Sjulian goto get_flags; 116411819Sjulian 116511819Sjulian case SO_HEADERS_ON_OUTPUT: 116611819Sjulian mask = SF_HO; 116711819Sjulian get_flags: 116838482Swollman soptval = cb->s_flags & mask; 116938482Swollman error = sooptcopyout(sopt, &soptval, sizeof soptval); 117011819Sjulian break; 117111819Sjulian 117211819Sjulian case SO_MTU: 117338482Swollman usoptval = cb->s_mtu; 117438482Swollman error = sooptcopyout(sopt, &usoptval, sizeof usoptval); 117511819Sjulian break; 117611819Sjulian 117711819Sjulian case SO_LAST_HEADER: 1178139584Srwatson error = sooptcopyout(sopt, &cb->s_rhdr, 117938482Swollman sizeof cb->s_rhdr); 118011819Sjulian break; 118111819Sjulian 118211819Sjulian case SO_DEFAULT_HEADERS: 1183139584Srwatson error = sooptcopyout(sopt, &cb->s_shdr, 118438482Swollman sizeof cb->s_shdr); 118511819Sjulian break; 118611819Sjulian 118711819Sjulian default: 118838482Swollman error = ENOPROTOOPT; 118911819Sjulian } 119011819Sjulian break; 119111819Sjulian 119238482Swollman case SOPT_SET: 119338482Swollman switch (sopt->sopt_name) { 119438482Swollman /* XXX why are these shorts on get and ints on set? 119538482Swollman that doesn't make any sense... */ 119611819Sjulian case SO_HEADERS_ON_INPUT: 119711819Sjulian mask = SF_HI; 119811819Sjulian goto set_head; 119911819Sjulian 120011819Sjulian case SO_HEADERS_ON_OUTPUT: 120111819Sjulian mask = SF_HO; 120211819Sjulian set_head: 120338482Swollman error = sooptcopyin(sopt, &optval, sizeof optval, 120438482Swollman sizeof optval); 120538482Swollman if (error) 120638482Swollman break; 120738482Swollman 120811819Sjulian if (cb->s_flags & SF_PI) { 120938482Swollman if (optval) 121011819Sjulian cb->s_flags |= mask; 121111819Sjulian else 121211819Sjulian cb->s_flags &= ~mask; 121311819Sjulian } else error = EINVAL; 121411819Sjulian break; 121511819Sjulian 121611819Sjulian case SO_MTU: 121738482Swollman error = sooptcopyin(sopt, &usoptval, sizeof usoptval, 121838482Swollman sizeof usoptval); 121938482Swollman if (error) 122038482Swollman break; 122138482Swollman cb->s_mtu = usoptval; 122211819Sjulian break; 122311819Sjulian 122411819Sjulian#ifdef SF_NEWCALL 122511819Sjulian case SO_NEWCALL: 122638482Swollman error = sooptcopyin(sopt, &optval, sizeof optval, 122738482Swollman sizeof optval); 122838482Swollman if (error) 122938482Swollman break; 123038482Swollman if (optval) { 123111819Sjulian cb->s_flags2 |= SF_NEWCALL; 123211819Sjulian spx_newchecks[5]++; 123311819Sjulian } else { 123411819Sjulian cb->s_flags2 &= ~SF_NEWCALL; 123511819Sjulian spx_newchecks[6]++; 123611819Sjulian } 123711819Sjulian break; 123811819Sjulian#endif 123911819Sjulian 124011819Sjulian case SO_DEFAULT_HEADERS: 124111819Sjulian { 124238482Swollman struct spxhdr sp; 124338482Swollman 124438482Swollman error = sooptcopyin(sopt, &sp, sizeof sp, 124538482Swollman sizeof sp); 124638482Swollman if (error) 124738482Swollman break; 124838482Swollman cb->s_dt = sp.spx_dt; 124938482Swollman cb->s_cc = sp.spx_cc & SPX_EM; 125011819Sjulian } 125111819Sjulian break; 125211819Sjulian 125311819Sjulian default: 125438482Swollman error = ENOPROTOOPT; 125511819Sjulian } 125611819Sjulian break; 125711819Sjulian } 125838482Swollman return (error); 125911819Sjulian} 126011819Sjulian 126124659Sjhaystatic int 126224659Sjhayspx_usr_abort(so) 126311819Sjulian struct socket *so; 126411819Sjulian{ 126524659Sjhay int s; 126624659Sjhay struct ipxpcb *ipxp; 126724659Sjhay struct spxpcb *cb; 126811819Sjulian 126924659Sjhay ipxp = sotoipxpcb(so); 127024659Sjhay cb = ipxtospxpcb(ipxp); 127111819Sjulian 127224659Sjhay s = splnet(); 127324659Sjhay spx_drop(cb, ECONNABORTED); 127424659Sjhay splx(s); 127524659Sjhay return (0); 127624659Sjhay} 127711819Sjulian 127824659Sjhay/* 127924659Sjhay * Accept a connection. Essentially all the work is 128024659Sjhay * done at higher levels; just return the address 128124659Sjhay * of the peer, storing through addr. 128224659Sjhay */ 128324659Sjhaystatic int 128424659Sjhayspx_accept(so, nam) 128524659Sjhay struct socket *so; 128628270Swollman struct sockaddr **nam; 128724659Sjhay{ 128824659Sjhay struct ipxpcb *ipxp; 128928270Swollman struct sockaddr_ipx *sipx, ssipx; 129011819Sjulian 129124659Sjhay ipxp = sotoipxpcb(so); 129228270Swollman sipx = &ssipx; 129328270Swollman bzero(sipx, sizeof *sipx); 129428270Swollman sipx->sipx_len = sizeof *sipx; 129524659Sjhay sipx->sipx_family = AF_IPX; 129624659Sjhay sipx->sipx_addr = ipxp->ipxp_faddr; 1297126425Srwatson *nam = sodupsockaddr((struct sockaddr *)sipx, M_NOWAIT); 129824659Sjhay return (0); 129924659Sjhay} 130024659Sjhay 130124659Sjhaystatic int 130283366Sjulianspx_attach(so, proto, td) 130324659Sjhay struct socket *so; 130424659Sjhay int proto; 130583366Sjulian struct thread *td; 130624659Sjhay{ 130724659Sjhay int error; 130824659Sjhay int s; 130924659Sjhay struct ipxpcb *ipxp; 131024659Sjhay struct spxpcb *cb; 131124659Sjhay struct mbuf *mm; 131224659Sjhay struct sockbuf *sb; 131324659Sjhay 131424659Sjhay ipxp = sotoipxpcb(so); 131524659Sjhay cb = ipxtospxpcb(ipxp); 131624659Sjhay 131724659Sjhay if (ipxp != NULL) 131824659Sjhay return (EISCONN); 131924659Sjhay s = splnet(); 1320139444Srwatson error = ipx_pcballoc(so, &ipxpcb_list, td); 132124659Sjhay if (error) 132224659Sjhay goto spx_attach_end; 132324659Sjhay if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 132424659Sjhay error = soreserve(so, (u_long) 3072, (u_long) 3072); 132511819Sjulian if (error) 132624659Sjhay goto spx_attach_end; 132724659Sjhay } 132824659Sjhay ipxp = sotoipxpcb(so); 132911819Sjulian 133069781Sdwmalone MALLOC(cb, struct spxpcb *, sizeof *cb, M_PCB, M_NOWAIT | M_ZERO); 133111819Sjulian 133228270Swollman if (cb == NULL) { 133324659Sjhay error = ENOBUFS; 133424659Sjhay goto spx_attach_end; 133524659Sjhay } 133643711Sjhay sb = &so->so_snd; 133728270Swollman 1338111119Simp mm = m_getclr(M_DONTWAIT, MT_HEADER); 133924659Sjhay if (mm == NULL) { 134028270Swollman FREE(cb, M_PCB); 134124659Sjhay error = ENOBUFS; 134224659Sjhay goto spx_attach_end; 134324659Sjhay } 134424659Sjhay cb->s_ipx = mtod(mm, struct ipx *); 134524659Sjhay cb->s_state = TCPS_LISTEN; 134624659Sjhay cb->s_smax = -1; 134724659Sjhay cb->s_swl1 = -1; 134824659Sjhay cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q; 134924659Sjhay cb->s_ipxpcb = ipxp; 135025652Sjhay cb->s_mtu = 576 - sizeof(struct spx); 135124659Sjhay cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu; 135224659Sjhay cb->s_ssthresh = cb->s_cwnd; 135325652Sjhay cb->s_cwmx = sbspace(sb) * CUNIT / (2 * sizeof(struct spx)); 135424659Sjhay /* Above is recomputed when connecting to account 135524659Sjhay for changed buffering or mtu's */ 135624659Sjhay cb->s_rtt = SPXTV_SRTTBASE; 135724659Sjhay cb->s_rttvar = SPXTV_SRTTDFLT << 2; 135824659Sjhay SPXT_RANGESET(cb->s_rxtcur, 135924659Sjhay ((SPXTV_SRTTBASE >> 2) + (SPXTV_SRTTDFLT << 2)) >> 1, 136024659Sjhay SPXTV_MIN, SPXTV_REXMTMAX); 1361139584Srwatson ipxp->ipxp_pcb = (caddr_t)cb; 136224659Sjhayspx_attach_end: 136324659Sjhay splx(s); 136424659Sjhay return (error); 136524659Sjhay} 136611819Sjulian 136724659Sjhaystatic int 136883366Sjulianspx_bind(so, nam, td) 136924659Sjhay struct socket *so; 137028270Swollman struct sockaddr *nam; 137183366Sjulian struct thread *td; 1372139584Srwatson{ 137324659Sjhay struct ipxpcb *ipxp; 137411819Sjulian 137524659Sjhay ipxp = sotoipxpcb(so); 137611819Sjulian 137783366Sjulian return (ipx_pcbbind(ipxp, nam, td)); 1378139584Srwatson} 1379139584Srwatson 138024659Sjhay/* 138124659Sjhay * Initiate connection to peer. 138224659Sjhay * Enter SYN_SENT state, and mark socket as connecting. 138324659Sjhay * Start keep-alive timer, setup prototype header, 138424659Sjhay * Send initial system packet requesting connection. 138524659Sjhay */ 138624659Sjhaystatic int 138783366Sjulianspx_connect(so, nam, td) 138824659Sjhay struct socket *so; 138928270Swollman struct sockaddr *nam; 139083366Sjulian struct thread *td; 139124659Sjhay{ 139224659Sjhay int error; 139324659Sjhay int s; 139424659Sjhay struct ipxpcb *ipxp; 139524659Sjhay struct spxpcb *cb; 139611819Sjulian 139724659Sjhay ipxp = sotoipxpcb(so); 139824659Sjhay cb = ipxtospxpcb(ipxp); 139924659Sjhay 140024659Sjhay s = splnet(); 140124659Sjhay if (ipxp->ipxp_lport == 0) { 1402139579Srwatson error = ipx_pcbbind(ipxp, NULL, td); 140324659Sjhay if (error) 140424659Sjhay goto spx_connect_end; 140524659Sjhay } 140683366Sjulian error = ipx_pcbconnect(ipxp, nam, td); 140724659Sjhay if (error) 140824659Sjhay goto spx_connect_end; 140924659Sjhay soisconnecting(so); 141024659Sjhay spxstat.spxs_connattempt++; 141124659Sjhay cb->s_state = TCPS_SYN_SENT; 141224659Sjhay cb->s_did = 0; 141324659Sjhay spx_template(cb); 141424659Sjhay cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 141524659Sjhay cb->s_force = 1 + SPXTV_KEEP; 141611819Sjulian /* 141724659Sjhay * Other party is required to respond to 141824659Sjhay * the port I send from, but he is not 141924659Sjhay * required to answer from where I am sending to, 142024659Sjhay * so allow wildcarding. 142124659Sjhay * original port I am sending to is still saved in 142224659Sjhay * cb->s_dport. 142311819Sjulian */ 142424659Sjhay ipxp->ipxp_fport = 0; 1425139579Srwatson error = spx_output(cb, NULL); 142624659Sjhayspx_connect_end: 142724659Sjhay splx(s); 142824659Sjhay return (error); 142924659Sjhay} 143011819Sjulian 143124659Sjhaystatic int 143224659Sjhayspx_detach(so) 143324659Sjhay struct socket *so; 143424659Sjhay{ 143524659Sjhay int s; 143624659Sjhay struct ipxpcb *ipxp; 143724659Sjhay struct spxpcb *cb; 143811819Sjulian 143924659Sjhay ipxp = sotoipxpcb(so); 144024659Sjhay cb = ipxtospxpcb(ipxp); 144111819Sjulian 144224659Sjhay if (ipxp == NULL) 144324659Sjhay return (ENOTCONN); 144424659Sjhay s = splnet(); 144524659Sjhay if (cb->s_state > TCPS_LISTEN) 144624659Sjhay spx_disconnect(cb); 144724659Sjhay else 144824659Sjhay spx_close(cb); 144924659Sjhay splx(s); 145024659Sjhay return (0); 145124659Sjhay} 145211819Sjulian 145324659Sjhay/* 145424659Sjhay * We may decide later to implement connection closing 145524659Sjhay * handshaking at the spx level optionally. 145624659Sjhay * here is the hook to do it: 145724659Sjhay */ 145824659Sjhaystatic int 145924659Sjhayspx_usr_disconnect(so) 146024659Sjhay struct socket *so; 146124659Sjhay{ 146224659Sjhay int s; 146324659Sjhay struct ipxpcb *ipxp; 146424659Sjhay struct spxpcb *cb; 146511819Sjulian 146624659Sjhay ipxp = sotoipxpcb(so); 146724659Sjhay cb = ipxtospxpcb(ipxp); 146811819Sjulian 146924659Sjhay s = splnet(); 147024659Sjhay spx_disconnect(cb); 147124659Sjhay splx(s); 147224659Sjhay return (0); 147324659Sjhay} 147411819Sjulian 147524659Sjhaystatic int 147683366Sjulianspx_listen(so, td) 147724659Sjhay struct socket *so; 147883366Sjulian struct thread *td; 147924659Sjhay{ 148024659Sjhay int error; 148124659Sjhay struct ipxpcb *ipxp; 148224659Sjhay struct spxpcb *cb; 148311819Sjulian 148424659Sjhay error = 0; 148524659Sjhay ipxp = sotoipxpcb(so); 148624659Sjhay cb = ipxtospxpcb(ipxp); 148711819Sjulian 148824659Sjhay if (ipxp->ipxp_lport == 0) 1489139579Srwatson error = ipx_pcbbind(ipxp, NULL, td); 149024659Sjhay if (error == 0) 149124659Sjhay cb->s_state = TCPS_LISTEN; 149224659Sjhay return (error); 149324659Sjhay} 149411819Sjulian 149524659Sjhay/* 149624659Sjhay * After a receive, possibly send acknowledgment 149724659Sjhay * updating allocation. 149824659Sjhay */ 149924659Sjhaystatic int 150024659Sjhayspx_rcvd(so, flags) 150124659Sjhay struct socket *so; 150224659Sjhay int flags; 150324659Sjhay{ 150424659Sjhay int s; 150524659Sjhay struct ipxpcb *ipxp; 150624659Sjhay struct spxpcb *cb; 150711819Sjulian 150824659Sjhay ipxp = sotoipxpcb(so); 150924659Sjhay cb = ipxtospxpcb(ipxp); 151011819Sjulian 151124659Sjhay s = splnet(); 151224659Sjhay cb->s_flags |= SF_RVD; 1513139579Srwatson spx_output(cb, NULL); 151424659Sjhay cb->s_flags &= ~SF_RVD; 151524659Sjhay splx(s); 151624659Sjhay return (0); 151724659Sjhay} 151811819Sjulian 151924659Sjhaystatic int 152024659Sjhayspx_rcvoob(so, m, flags) 152124659Sjhay struct socket *so; 152224659Sjhay struct mbuf *m; 152324659Sjhay int flags; 152424659Sjhay{ 152524659Sjhay struct ipxpcb *ipxp; 152624659Sjhay struct spxpcb *cb; 152711819Sjulian 152824659Sjhay ipxp = sotoipxpcb(so); 152924659Sjhay cb = ipxtospxpcb(ipxp); 153011819Sjulian 153124659Sjhay if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark || 1532130480Srwatson (so->so_rcv.sb_state & SBS_RCVATMARK)) { 153324659Sjhay m->m_len = 1; 153424659Sjhay *mtod(m, caddr_t) = cb->s_iobc; 153524659Sjhay return (0); 153611819Sjulian } 153724659Sjhay return (EINVAL); 153824659Sjhay} 153924659Sjhay 154024659Sjhaystatic int 154183366Sjulianspx_send(so, flags, m, addr, controlp, td) 154224659Sjhay struct socket *so; 154324659Sjhay int flags; 154424659Sjhay struct mbuf *m; 154528270Swollman struct sockaddr *addr; 154624659Sjhay struct mbuf *controlp; 154783366Sjulian struct thread *td; 154824659Sjhay{ 154924659Sjhay int error; 155024659Sjhay int s; 155124659Sjhay struct ipxpcb *ipxp; 155224659Sjhay struct spxpcb *cb; 155324659Sjhay 155424659Sjhay error = 0; 155524659Sjhay ipxp = sotoipxpcb(so); 155624659Sjhay cb = ipxtospxpcb(ipxp); 155724659Sjhay 155824659Sjhay s = splnet(); 155924659Sjhay if (flags & PRUS_OOB) { 156024659Sjhay if (sbspace(&so->so_snd) < -512) { 156124659Sjhay error = ENOBUFS; 156224659Sjhay goto spx_send_end; 156324659Sjhay } 156424659Sjhay cb->s_oobflags |= SF_SOOB; 156524659Sjhay } 156625652Sjhay if (controlp != NULL) { 156724659Sjhay u_short *p = mtod(controlp, u_short *); 156824659Sjhay spx_newchecks[2]++; 156925652Sjhay if ((p[0] == 5) && (p[1] == 1)) { /* XXXX, for testing */ 157024659Sjhay cb->s_shdr.spx_dt = *(u_char *)(&p[2]); 157124659Sjhay spx_newchecks[3]++; 157224659Sjhay } 157324659Sjhay m_freem(controlp); 157424659Sjhay } 157524659Sjhay controlp = NULL; 157624659Sjhay error = spx_output(cb, m); 157724659Sjhay m = NULL; 157824659Sjhayspx_send_end: 157911819Sjulian if (controlp != NULL) 158011819Sjulian m_freem(controlp); 158111819Sjulian if (m != NULL) 158211819Sjulian m_freem(m); 158311819Sjulian splx(s); 158411819Sjulian return (error); 158511819Sjulian} 158611819Sjulian 158724659Sjhaystatic int 158824659Sjhayspx_shutdown(so) 1589139584Srwatson struct socket *so; 159024659Sjhay{ 159124659Sjhay int error; 159224659Sjhay int s; 159324659Sjhay struct ipxpcb *ipxp; 159424659Sjhay struct spxpcb *cb; 159524659Sjhay 159624659Sjhay error = 0; 159724659Sjhay ipxp = sotoipxpcb(so); 159824659Sjhay cb = ipxtospxpcb(ipxp); 159924659Sjhay 160024659Sjhay s = splnet(); 160124659Sjhay socantsendmore(so); 160224659Sjhay cb = spx_usrclosed(cb); 160325652Sjhay if (cb != NULL) 1604139579Srwatson error = spx_output(cb, NULL); 160524659Sjhay splx(s); 160624659Sjhay return (error); 160724659Sjhay} 160824659Sjhay 160924659Sjhaystatic int 161083366Sjulianspx_sp_attach(so, proto, td) 161111819Sjulian struct socket *so; 161224659Sjhay int proto; 161383366Sjulian struct thread *td; 161411819Sjulian{ 161524659Sjhay int error; 161624659Sjhay struct ipxpcb *ipxp; 161711819Sjulian 161883366Sjulian error = spx_attach(so, proto, td); 161924659Sjhay if (error == 0) { 162024659Sjhay ipxp = sotoipxpcb(so); 162111819Sjulian ((struct spxpcb *)ipxp->ipxp_pcb)->s_flags |= 162211819Sjulian (SF_HI | SF_HO | SF_PI); 162311819Sjulian } 162411819Sjulian return (error); 162511819Sjulian} 162611819Sjulian 162711819Sjulian/* 162811819Sjulian * Create template to be used to send spx packets on a connection. 162911819Sjulian * Called after host entry created, fills 163011819Sjulian * in a skeletal spx header (choosing connection id), 163111819Sjulian * minimizing the amount of work necessary when the connection is used. 163211819Sjulian */ 163325652Sjhaystatic void 163411819Sjulianspx_template(cb) 163511819Sjulian register struct spxpcb *cb; 163611819Sjulian{ 163711819Sjulian register struct ipxpcb *ipxp = cb->s_ipxpcb; 163811819Sjulian register struct ipx *ipx = cb->s_ipx; 163911819Sjulian register struct sockbuf *sb = &(ipxp->ipxp_socket->so_snd); 164011819Sjulian 164111819Sjulian ipx->ipx_pt = IPXPROTO_SPX; 164211819Sjulian ipx->ipx_sna = ipxp->ipxp_laddr; 164311819Sjulian ipx->ipx_dna = ipxp->ipxp_faddr; 164411819Sjulian cb->s_sid = htons(spx_iss); 164511819Sjulian spx_iss += SPX_ISSINCR/2; 164611819Sjulian cb->s_alo = 1; 164711819Sjulian cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu; 164811819Sjulian cb->s_ssthresh = cb->s_cwnd; /* Try to expand fast to full complement 164911819Sjulian of large packets */ 165011819Sjulian cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spx)); 165111819Sjulian cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd); 165211819Sjulian /* But allow for lots of little packets as well */ 165311819Sjulian} 165411819Sjulian 165511819Sjulian/* 165611819Sjulian * Close a SPIP control block: 165711819Sjulian * discard spx control block itself 165811819Sjulian * discard ipx protocol control block 165911819Sjulian * wake up any sleepers 166011819Sjulian */ 166125652Sjhaystatic struct spxpcb * 166211819Sjulianspx_close(cb) 166311819Sjulian register struct spxpcb *cb; 166411819Sjulian{ 166511819Sjulian register struct spx_q *s; 166611819Sjulian struct ipxpcb *ipxp = cb->s_ipxpcb; 166711819Sjulian struct socket *so = ipxp->ipxp_socket; 166811819Sjulian register struct mbuf *m; 166911819Sjulian 167011819Sjulian s = cb->s_q.si_next; 167111819Sjulian while (s != &(cb->s_q)) { 167211819Sjulian s = s->si_next; 167311819Sjulian m = dtom(s->si_prev); 167411819Sjulian remque(s->si_prev); 167511819Sjulian m_freem(m); 167611819Sjulian } 167725652Sjhay m_free(dtom(cb->s_ipx)); 167828270Swollman FREE(cb, M_PCB); 1679139580Srwatson ipxp->ipxp_pcb = NULL; 168011819Sjulian soisdisconnected(so); 168111819Sjulian ipx_pcbdetach(ipxp); 168211819Sjulian spxstat.spxs_closed++; 1683139579Srwatson return (NULL); 168411819Sjulian} 168525652Sjhay 168611819Sjulian/* 168711819Sjulian * Someday we may do level 3 handshaking 168811819Sjulian * to close a connection or send a xerox style error. 168911819Sjulian * For now, just close. 169011819Sjulian */ 169125652Sjhaystatic struct spxpcb * 169211819Sjulianspx_usrclosed(cb) 169311819Sjulian register struct spxpcb *cb; 169411819Sjulian{ 169511819Sjulian return (spx_close(cb)); 169611819Sjulian} 169725652Sjhay 169825652Sjhaystatic struct spxpcb * 169911819Sjulianspx_disconnect(cb) 170011819Sjulian register struct spxpcb *cb; 170111819Sjulian{ 170211819Sjulian return (spx_close(cb)); 170311819Sjulian} 170425652Sjhay 170511819Sjulian/* 170611819Sjulian * Drop connection, reporting 170711819Sjulian * the specified error. 170811819Sjulian */ 170925652Sjhaystatic struct spxpcb * 171011819Sjulianspx_drop(cb, errno) 171111819Sjulian register struct spxpcb *cb; 171211819Sjulian int errno; 171311819Sjulian{ 171411819Sjulian struct socket *so = cb->s_ipxpcb->ipxp_socket; 171511819Sjulian 171611819Sjulian /* 171711819Sjulian * someday, in the xerox world 171811819Sjulian * we will generate error protocol packets 171911819Sjulian * announcing that the socket has gone away. 172011819Sjulian */ 172111819Sjulian if (TCPS_HAVERCVDSYN(cb->s_state)) { 172211819Sjulian spxstat.spxs_drops++; 172311819Sjulian cb->s_state = TCPS_CLOSED; 172425652Sjhay /*tcp_output(cb);*/ 172511819Sjulian } else 172611819Sjulian spxstat.spxs_conndrops++; 172711819Sjulian so->so_error = errno; 172811819Sjulian return (spx_close(cb)); 172911819Sjulian} 173011819Sjulian 173111819Sjulian/* 173211819Sjulian * Fast timeout routine for processing delayed acks 173311819Sjulian */ 173411819Sjulianvoid 173511819Sjulianspx_fasttimo() 173611819Sjulian{ 173711819Sjulian register struct ipxpcb *ipxp; 173811819Sjulian register struct spxpcb *cb; 173911819Sjulian int s = splnet(); 174011819Sjulian 1741139444Srwatson LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) { 174225652Sjhay if ((cb = (struct spxpcb *)ipxp->ipxp_pcb) != NULL && 174311819Sjulian (cb->s_flags & SF_DELACK)) { 174411819Sjulian cb->s_flags &= ~SF_DELACK; 174511819Sjulian cb->s_flags |= SF_ACKNOW; 174611819Sjulian spxstat.spxs_delack++; 1747139579Srwatson spx_output(cb, NULL); 174811819Sjulian } 1749139444Srwatson } 1750139444Srwatson 175111819Sjulian splx(s); 175211819Sjulian} 175311819Sjulian 175411819Sjulian/* 175511819Sjulian * spx protocol timeout routine called every 500 ms. 175611819Sjulian * Updates the timers in all active pcb's and 175711819Sjulian * causes finite state machine actions if timers expire. 175811819Sjulian */ 175911819Sjulianvoid 176011819Sjulianspx_slowtimo() 176111819Sjulian{ 1762139444Srwatson register struct ipxpcb *ip, *ip_temp; 176311819Sjulian register struct spxpcb *cb; 176411819Sjulian int s = splnet(); 176511819Sjulian register int i; 176611819Sjulian 176711819Sjulian /* 1768139444Srwatson * Search through tcb's and update active timers. Note that timers 1769139444Srwatson * may free the ipxpcb, so be sure to handle that case. 177011819Sjulian */ 1771139444Srwatson LIST_FOREACH_SAFE(ip, &ipxpcb_list, ipxp_list, ip_temp) { 177211819Sjulian cb = ipxtospxpcb(ip); 177325652Sjhay if (cb == NULL) 1774139444Srwatson continue; 177511819Sjulian for (i = 0; i < SPXT_NTIMERS; i++) { 177611819Sjulian if (cb->s_timer[i] && --cb->s_timer[i] == 0) { 1777139444Srwatson /* 1778139444Srwatson * spx_timers() returns (NULL) if it free'd 1779139444Srwatson * the pcb. 1780139444Srwatson */ 1781139581Srwatson cb = spx_timers(cb, i); 1782139581Srwatson if (cb == NULL) 1783139581Srwatson break; 178411819Sjulian } 178511819Sjulian } 1786139581Srwatson if (cb != NULL) { 1787139581Srwatson cb->s_idle++; 1788139581Srwatson if (cb->s_rtt) 1789139581Srwatson cb->s_rtt++; 1790139581Srwatson } 179111819Sjulian } 179211819Sjulian spx_iss += SPX_ISSINCR/PR_SLOWHZ; /* increment iss */ 179311819Sjulian splx(s); 179411819Sjulian} 179525652Sjhay 179611819Sjulian/* 179711819Sjulian * SPX timer processing. 179811819Sjulian */ 179925652Sjhaystatic struct spxpcb * 180011819Sjulianspx_timers(cb, timer) 180111819Sjulian register struct spxpcb *cb; 180211819Sjulian int timer; 180311819Sjulian{ 180411819Sjulian long rexmt; 180511819Sjulian int win; 180611819Sjulian 180711819Sjulian cb->s_force = 1 + timer; 180811819Sjulian switch (timer) { 180911819Sjulian 181011819Sjulian /* 181111819Sjulian * 2 MSL timeout in shutdown went off. TCP deletes connection 181211819Sjulian * control block. 181311819Sjulian */ 181411819Sjulian case SPXT_2MSL: 181511819Sjulian printf("spx: SPXT_2MSL went off for no reason\n"); 181611819Sjulian cb->s_timer[timer] = 0; 181711819Sjulian break; 181811819Sjulian 181911819Sjulian /* 182011819Sjulian * Retransmission timer went off. Message has not 182111819Sjulian * been acked within retransmit interval. Back off 182211819Sjulian * to a longer retransmit interval and retransmit one packet. 182311819Sjulian */ 182411819Sjulian case SPXT_REXMT: 182511819Sjulian if (++cb->s_rxtshift > SPX_MAXRXTSHIFT) { 182611819Sjulian cb->s_rxtshift = SPX_MAXRXTSHIFT; 182711819Sjulian spxstat.spxs_timeoutdrop++; 182811819Sjulian cb = spx_drop(cb, ETIMEDOUT); 182911819Sjulian break; 183011819Sjulian } 183111819Sjulian spxstat.spxs_rexmttimeo++; 183211819Sjulian rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1; 183311819Sjulian rexmt *= spx_backoff[cb->s_rxtshift]; 183411819Sjulian SPXT_RANGESET(cb->s_rxtcur, rexmt, SPXTV_MIN, SPXTV_REXMTMAX); 183511819Sjulian cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 183611819Sjulian /* 183711819Sjulian * If we have backed off fairly far, our srtt 183811819Sjulian * estimate is probably bogus. Clobber it 183911819Sjulian * so we'll take the next rtt measurement as our srtt; 184011819Sjulian * move the current srtt into rttvar to keep the current 184111819Sjulian * retransmit times until then. 184211819Sjulian */ 184311819Sjulian if (cb->s_rxtshift > SPX_MAXRXTSHIFT / 4 ) { 184411819Sjulian cb->s_rttvar += (cb->s_srtt >> 2); 184511819Sjulian cb->s_srtt = 0; 184611819Sjulian } 184711819Sjulian cb->s_snxt = cb->s_rack; 184811819Sjulian /* 184911819Sjulian * If timing a packet, stop the timer. 185011819Sjulian */ 185111819Sjulian cb->s_rtt = 0; 185211819Sjulian /* 185311819Sjulian * See very long discussion in tcp_timer.c about congestion 185411819Sjulian * window and sstrhesh 185511819Sjulian */ 185611819Sjulian win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2; 185711819Sjulian if (win < 2) 185811819Sjulian win = 2; 185911819Sjulian cb->s_cwnd = CUNIT; 186011819Sjulian cb->s_ssthresh = win * CUNIT; 1861139579Srwatson spx_output(cb, NULL); 186211819Sjulian break; 186311819Sjulian 186411819Sjulian /* 186511819Sjulian * Persistance timer into zero window. 186611819Sjulian * Force a probe to be sent. 186711819Sjulian */ 186811819Sjulian case SPXT_PERSIST: 186911819Sjulian spxstat.spxs_persisttimeo++; 187011819Sjulian spx_setpersist(cb); 1871139579Srwatson spx_output(cb, NULL); 187211819Sjulian break; 187311819Sjulian 187411819Sjulian /* 187511819Sjulian * Keep-alive timer went off; send something 187611819Sjulian * or drop connection if idle for too long. 187711819Sjulian */ 187811819Sjulian case SPXT_KEEP: 187911819Sjulian spxstat.spxs_keeptimeo++; 188011819Sjulian if (cb->s_state < TCPS_ESTABLISHED) 188111819Sjulian goto dropit; 188211819Sjulian if (cb->s_ipxpcb->ipxp_socket->so_options & SO_KEEPALIVE) { 188311819Sjulian if (cb->s_idle >= SPXTV_MAXIDLE) 188411819Sjulian goto dropit; 188511819Sjulian spxstat.spxs_keepprobe++; 1886139579Srwatson spx_output(cb, NULL); 188797658Stanimura } else 188811819Sjulian cb->s_idle = 0; 188911819Sjulian cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 189011819Sjulian break; 189111819Sjulian dropit: 189211819Sjulian spxstat.spxs_keepdrops++; 189311819Sjulian cb = spx_drop(cb, ETIMEDOUT); 189411819Sjulian break; 189511819Sjulian } 189611819Sjulian return (cb); 189711819Sjulian} 1898