spx_reass.c revision 157068
1139823Simp/*-
2157067Srwatson * Copyright (c) 1984, 1985, 1986, 1987, 1993
3157067Srwatson *	The Regents of the University of California.
411819Sjulian * Copyright (c) 1995, Mike Mitchell
5157067Srwatson * Copyright (c) 2004-2006 Robert N. M. Watson
6157067Srwatson * All rights reserved.
711819Sjulian *
811819Sjulian * Redistribution and use in source and binary forms, with or without
911819Sjulian * modification, are permitted provided that the following conditions
1011819Sjulian * are met:
1111819Sjulian * 1. Redistributions of source code must retain the above copyright
1211819Sjulian *    notice, this list of conditions and the following disclaimer.
1311819Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1411819Sjulian *    notice, this list of conditions and the following disclaimer in the
1511819Sjulian *    documentation and/or other materials provided with the distribution.
1611819Sjulian * 3. All advertising materials mentioning features or use of this software
1711819Sjulian *    must display the following acknowledgement:
1811819Sjulian *	This product includes software developed by the University of
1911819Sjulian *	California, Berkeley and its contributors.
2011819Sjulian * 4. Neither the name of the University nor the names of its contributors
2111819Sjulian *    may be used to endorse or promote products derived from this software
2211819Sjulian *    without specific prior written permission.
2311819Sjulian *
2411819Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2511819Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2611819Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2711819Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2811819Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2911819Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3011819Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3111819Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3211819Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3311819Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3411819Sjulian * SUCH DAMAGE.
3511819Sjulian *
3612057Sjulian *	@(#)spx_usrreq.h
3711819Sjulian */
3811819Sjulian
39116189Sobrien#include <sys/cdefs.h>
40116189Sobrien__FBSDID("$FreeBSD: head/sys/netipx/spx_usrreq.c 157068 2006-03-24 00:22:25Z rwatson $");
41116189Sobrien
4211819Sjulian#include <sys/param.h>
4376166Smarkm#include <sys/lock.h>
4429024Sbde#include <sys/malloc.h>
4511819Sjulian#include <sys/mbuf.h>
4676166Smarkm#include <sys/mutex.h>
4725345Sjhay#include <sys/proc.h>
4811819Sjulian#include <sys/protosw.h>
4995759Stanimura#include <sys/signalvar.h>
5011819Sjulian#include <sys/socket.h>
5111819Sjulian#include <sys/socketvar.h>
5295759Stanimura#include <sys/sx.h>
5395759Stanimura#include <sys/systm.h>
5411819Sjulian
5511819Sjulian#include <net/route.h>
5611819Sjulian#include <netinet/tcp_fsm.h>
5711819Sjulian
5811819Sjulian#include <netipx/ipx.h>
5911819Sjulian#include <netipx/ipx_pcb.h>
6011819Sjulian#include <netipx/ipx_var.h>
6111819Sjulian#include <netipx/spx.h>
6295759Stanimura#include <netipx/spx_debug.h>
6311819Sjulian#include <netipx/spx_timer.h>
6411819Sjulian#include <netipx/spx_var.h>
6511819Sjulian
6611819Sjulian/*
6711819Sjulian * SPX protocol implementation.
6811819Sjulian */
6933181Seivindstatic u_short 	spx_iss;
7033181Seivindstatic u_short	spx_newchecks[50];
7133181Seivindstatic int	spx_hardnosed;
7233181Seivindstatic int	spx_use_delack = 0;
7333181Seivindstatic int	traceallspxs = 0;
7433181Seivindstatic struct	spx_istat spx_istat;
75157068Srwatsonstatic int	spxrexmtthresh = 3;
7611819Sjulian
7725652Sjhay/* Following was struct spxstat spxstat; */
78139584Srwatson#ifndef spxstat
7925652Sjhay#define spxstat spx_istat.newstats
80139584Srwatson#endif
8111819Sjulian
82132045Srwatsonstatic const int spx_backoff[SPX_MAXRXTSHIFT+1] =
8325652Sjhay    { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
8411819Sjulian
85139931Srwatsonstatic	void spx_close(struct spxpcb *cb);
86139931Srwatsonstatic	void spx_disconnect(struct spxpcb *cb);
87139931Srwatsonstatic	void spx_drop(struct spxpcb *cb, int errno);
8825652Sjhaystatic	int spx_output(struct spxpcb *cb, struct mbuf *m0);
8925652Sjhaystatic	int spx_reass(struct spxpcb *cb, struct spx *si);
9025652Sjhaystatic	void spx_setpersist(struct spxpcb *cb);
9125652Sjhaystatic	void spx_template(struct spxpcb *cb);
9225652Sjhaystatic	struct spxpcb *spx_timers(struct spxpcb *cb, int timer);
93139931Srwatsonstatic	void spx_usrclosed(struct spxpcb *cb);
9425652Sjhay
9524659Sjhaystatic	int spx_usr_abort(struct socket *so);
9628270Swollmanstatic	int spx_accept(struct socket *so, struct sockaddr **nam);
9783366Sjulianstatic	int spx_attach(struct socket *so, int proto, struct thread *td);
9883366Sjulianstatic	int spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td);
9928270Swollmanstatic	int spx_connect(struct socket *so, struct sockaddr *nam,
10083366Sjulian			struct thread *td);
10124659Sjhaystatic	int spx_detach(struct socket *so);
10224659Sjhaystatic	int spx_usr_disconnect(struct socket *so);
103151888Srwatsonstatic	int spx_listen(struct socket *so, int backlog, struct thread *td);
10424659Sjhaystatic	int spx_rcvd(struct socket *so, int flags);
10524659Sjhaystatic	int spx_rcvoob(struct socket *so, struct mbuf *m, int flags);
10624659Sjhaystatic	int spx_send(struct socket *so, int flags, struct mbuf *m,
107139584Srwatson		     struct sockaddr *addr, struct mbuf *control,
10883366Sjulian		     struct thread *td);
10924659Sjhaystatic	int spx_shutdown(struct socket *so);
11083366Sjulianstatic	int spx_sp_attach(struct socket *so, int proto, struct thread *td);
11124659Sjhay
11224659Sjhaystruct	pr_usrreqs spx_usrreqs = {
113137386Sphk	.pru_abort =		spx_usr_abort,
114137386Sphk	.pru_accept =		spx_accept,
115137386Sphk	.pru_attach =		spx_attach,
116137386Sphk	.pru_bind =		spx_bind,
117137386Sphk	.pru_connect =		spx_connect,
118137386Sphk	.pru_control =		ipx_control,
119137386Sphk	.pru_detach =		spx_detach,
120137386Sphk	.pru_disconnect =	spx_usr_disconnect,
121137386Sphk	.pru_listen =		spx_listen,
122137386Sphk	.pru_peeraddr =		ipx_peeraddr,
123137386Sphk	.pru_rcvd =		spx_rcvd,
124137386Sphk	.pru_rcvoob =		spx_rcvoob,
125137386Sphk	.pru_send =		spx_send,
126137386Sphk	.pru_shutdown =		spx_shutdown,
127137386Sphk	.pru_sockaddr =		ipx_sockaddr,
12824659Sjhay};
12924659Sjhay
13024659Sjhaystruct	pr_usrreqs spx_usrreq_sps = {
131137386Sphk	.pru_abort =		spx_usr_abort,
132137386Sphk	.pru_accept =		spx_accept,
133137386Sphk	.pru_attach =		spx_sp_attach,
134137386Sphk	.pru_bind =		spx_bind,
135137386Sphk	.pru_connect =		spx_connect,
136137386Sphk	.pru_control =		ipx_control,
137137386Sphk	.pru_detach =		spx_detach,
138137386Sphk	.pru_disconnect =	spx_usr_disconnect,
139137386Sphk	.pru_listen =		spx_listen,
140137386Sphk	.pru_peeraddr =		ipx_peeraddr,
141137386Sphk	.pru_rcvd =		spx_rcvd,
142137386Sphk	.pru_rcvoob =		spx_rcvoob,
143137386Sphk	.pru_send =		spx_send,
144137386Sphk	.pru_shutdown =		spx_shutdown,
145137386Sphk	.pru_sockaddr =		ipx_sockaddr,
14624659Sjhay};
14724659Sjhay
14811819Sjulianvoid
149157067Srwatsonspx_init(void)
15011819Sjulian{
15111819Sjulian
15211819Sjulian	spx_iss = 1; /* WRONG !! should fish it out of TODR */
15311819Sjulian}
15411819Sjulian
15511819Sjulianvoid
156157067Srwatsonspx_input(struct mbuf *m, struct ipxpcb *ipxp)
15711819Sjulian{
158157067Srwatson	struct spxpcb *cb;
159157067Srwatson	struct spx *si = mtod(m, struct spx *);
160157067Srwatson	struct socket *so;
161157051Srwatson	struct spx spx_savesi;
16211819Sjulian	int dropsocket = 0;
16311819Sjulian	short ostate = 0;
16411819Sjulian
16511819Sjulian	spxstat.spxs_rcvtotal++;
166139586Srwatson	KASSERT(ipxp != NULL, ("spx_input: NULL ipxpcb"));
16711819Sjulian
168139932Srwatson	/*
169139932Srwatson	 * spx_input() assumes that the caller will hold both the pcb list
170139932Srwatson	 * lock and also the ipxp lock.  spx_input() will release both before
171139932Srwatson	 * returning, and may in fact trade in the ipxp lock for another pcb
172139932Srwatson	 * lock following sonewconn().
173139932Srwatson	 */
174139932Srwatson	IPX_LIST_LOCK_ASSERT();
175139932Srwatson	IPX_LOCK_ASSERT(ipxp);
176139932Srwatson
17711819Sjulian	cb = ipxtospxpcb(ipxp);
17825652Sjhay	if (cb == NULL)
17925652Sjhay		goto bad;
18011819Sjulian
18111819Sjulian	if (m->m_len < sizeof(*si)) {
18225652Sjhay		if ((m = m_pullup(m, sizeof(*si))) == NULL) {
183139932Srwatson			IPX_UNLOCK(ipxp);
184139932Srwatson			IPX_LIST_UNLOCK();
18511819Sjulian			spxstat.spxs_rcvshort++;
18611819Sjulian			return;
18711819Sjulian		}
18811819Sjulian		si = mtod(m, struct spx *);
18911819Sjulian	}
19011819Sjulian	si->si_seq = ntohs(si->si_seq);
19111819Sjulian	si->si_ack = ntohs(si->si_ack);
19211819Sjulian	si->si_alo = ntohs(si->si_alo);
19311819Sjulian
19411819Sjulian	so = ipxp->ipxp_socket;
19511819Sjulian
19611819Sjulian	if (so->so_options & SO_DEBUG || traceallspxs) {
19711819Sjulian		ostate = cb->s_state;
19811819Sjulian		spx_savesi = *si;
19911819Sjulian	}
20011819Sjulian	if (so->so_options & SO_ACCEPTCONN) {
20111819Sjulian		struct spxpcb *ocb = cb;
20211819Sjulian
20311819Sjulian		so = sonewconn(so, 0);
20425652Sjhay		if (so == NULL) {
20511819Sjulian			goto drop;
20611819Sjulian		}
20711819Sjulian		/*
20811819Sjulian		 * This is ugly, but ....
20911819Sjulian		 *
21011819Sjulian		 * Mark socket as temporary until we're
21111819Sjulian		 * committed to keeping it.  The code at
21211819Sjulian		 * ``drop'' and ``dropwithreset'' check the
21311819Sjulian		 * flag dropsocket to see if the temporary
21411819Sjulian		 * socket created here should be discarded.
21511819Sjulian		 * We mark the socket as discardable until
21611819Sjulian		 * we're committed to it below in TCPS_LISTEN.
21711819Sjulian		 */
21811819Sjulian		dropsocket++;
219139932Srwatson		IPX_UNLOCK(ipxp);
22011819Sjulian		ipxp = (struct ipxpcb *)so->so_pcb;
221139932Srwatson		IPX_LOCK(ipxp);
22211819Sjulian		ipxp->ipxp_laddr = si->si_dna;
22311819Sjulian		cb = ipxtospxpcb(ipxp);
22411819Sjulian		cb->s_mtu = ocb->s_mtu;		/* preserve sockopts */
22511819Sjulian		cb->s_flags = ocb->s_flags;	/* preserve sockopts */
22611819Sjulian		cb->s_flags2 = ocb->s_flags2;	/* preserve sockopts */
22711819Sjulian		cb->s_state = TCPS_LISTEN;
22897658Stanimura	}
22911819Sjulian
23011819Sjulian	/*
23111819Sjulian	 * Packet received on connection.
23211819Sjulian	 * reset idle time and keep-alive timer;
23311819Sjulian	 */
23411819Sjulian	cb->s_idle = 0;
23511819Sjulian	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
23611819Sjulian
23711819Sjulian	switch (cb->s_state) {
23811819Sjulian
23911819Sjulian	case TCPS_LISTEN:{
24028270Swollman		struct sockaddr_ipx *sipx, ssipx;
24111819Sjulian		struct ipx_addr laddr;
24211819Sjulian
24311819Sjulian		/*
24411819Sjulian		 * If somebody here was carying on a conversation
24511819Sjulian		 * and went away, and his pen pal thinks he can
24611819Sjulian		 * still talk, we get the misdirected packet.
24711819Sjulian		 */
24811819Sjulian		if (spx_hardnosed && (si->si_did != 0 || si->si_seq != 0)) {
24911819Sjulian			spx_istat.gonawy++;
25011819Sjulian			goto dropwithreset;
25111819Sjulian		}
25228270Swollman		sipx = &ssipx;
25328270Swollman		bzero(sipx, sizeof *sipx);
25411819Sjulian		sipx->sipx_len = sizeof(*sipx);
25511819Sjulian		sipx->sipx_family = AF_IPX;
25611819Sjulian		sipx->sipx_addr = si->si_sna;
25711819Sjulian		laddr = ipxp->ipxp_laddr;
25811819Sjulian		if (ipx_nullhost(laddr))
25911819Sjulian			ipxp->ipxp_laddr = si->si_dna;
26090361Sjulian		if (ipx_pcbconnect(ipxp, (struct sockaddr *)sipx, &thread0)) {
26111819Sjulian			ipxp->ipxp_laddr = laddr;
26211819Sjulian			spx_istat.noconn++;
26311819Sjulian			goto drop;
26411819Sjulian		}
26511819Sjulian		spx_template(cb);
26611819Sjulian		dropsocket = 0;		/* committed to socket */
26711819Sjulian		cb->s_did = si->si_sid;
26811819Sjulian		cb->s_rack = si->si_ack;
26911819Sjulian		cb->s_ralo = si->si_alo;
27011819Sjulian#define THREEWAYSHAKE
27111819Sjulian#ifdef THREEWAYSHAKE
27211819Sjulian		cb->s_state = TCPS_SYN_RECEIVED;
27311819Sjulian		cb->s_force = 1 + SPXT_KEEP;
27411819Sjulian		spxstat.spxs_accepts++;
27511819Sjulian		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
27611819Sjulian		}
27711819Sjulian		break;
27811819Sjulian	/*
27911819Sjulian	 * This state means that we have heard a response
28011819Sjulian	 * to our acceptance of their connection
28111819Sjulian	 * It is probably logically unnecessary in this
28211819Sjulian	 * implementation.
28311819Sjulian	 */
28411819Sjulian	 case TCPS_SYN_RECEIVED: {
28525652Sjhay		if (si->si_did != cb->s_sid) {
28611819Sjulian			spx_istat.wrncon++;
28711819Sjulian			goto drop;
28811819Sjulian		}
28911819Sjulian#endif
29011819Sjulian		ipxp->ipxp_fport =  si->si_sport;
29111819Sjulian		cb->s_timer[SPXT_REXMT] = 0;
29211819Sjulian		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
29311819Sjulian		soisconnected(so);
29411819Sjulian		cb->s_state = TCPS_ESTABLISHED;
29511819Sjulian		spxstat.spxs_accepts++;
29611819Sjulian		}
29711819Sjulian		break;
29811819Sjulian
29911819Sjulian	/*
30011819Sjulian	 * This state means that we have gotten a response
30111819Sjulian	 * to our attempt to establish a connection.
30211819Sjulian	 * We fill in the data from the other side,
30311819Sjulian	 * telling us which port to respond to, instead of the well-
30411819Sjulian	 * known one we might have sent to in the first place.
30511819Sjulian	 * We also require that this is a response to our
30611819Sjulian	 * connection id.
30711819Sjulian	 */
30811819Sjulian	case TCPS_SYN_SENT:
30925652Sjhay		if (si->si_did != cb->s_sid) {
31011819Sjulian			spx_istat.notme++;
31111819Sjulian			goto drop;
31211819Sjulian		}
31311819Sjulian		spxstat.spxs_connects++;
31411819Sjulian		cb->s_did = si->si_sid;
31511819Sjulian		cb->s_rack = si->si_ack;
31611819Sjulian		cb->s_ralo = si->si_alo;
31711819Sjulian		cb->s_dport = ipxp->ipxp_fport =  si->si_sport;
31811819Sjulian		cb->s_timer[SPXT_REXMT] = 0;
31911819Sjulian		cb->s_flags |= SF_ACKNOW;
32011819Sjulian		soisconnected(so);
32111819Sjulian		cb->s_state = TCPS_ESTABLISHED;
32211819Sjulian		/* Use roundtrip time of connection request for initial rtt */
32311819Sjulian		if (cb->s_rtt) {
32411819Sjulian			cb->s_srtt = cb->s_rtt << 3;
32511819Sjulian			cb->s_rttvar = cb->s_rtt << 1;
32611819Sjulian			SPXT_RANGESET(cb->s_rxtcur,
32711819Sjulian			    ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
32811819Sjulian			    SPXTV_MIN, SPXTV_REXMTMAX);
32911819Sjulian			    cb->s_rtt = 0;
33011819Sjulian		}
33111819Sjulian	}
33297658Stanimura	if (so->so_options & SO_DEBUG || traceallspxs)
33311819Sjulian		spx_trace(SA_INPUT, (u_char)ostate, cb, &spx_savesi, 0);
33411819Sjulian
33525652Sjhay	m->m_len -= sizeof(struct ipx);
33625652Sjhay	m->m_pkthdr.len -= sizeof(struct ipx);
33725652Sjhay	m->m_data += sizeof(struct ipx);
33811819Sjulian
33911819Sjulian	if (spx_reass(cb, si)) {
34025652Sjhay		m_freem(m);
34111819Sjulian	}
34211819Sjulian	if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT)))
343139579Srwatson		spx_output(cb, NULL);
34411819Sjulian	cb->s_flags &= ~(SF_WIN|SF_RXT);
345139932Srwatson	IPX_UNLOCK(ipxp);
346139932Srwatson	IPX_LIST_UNLOCK();
34711819Sjulian	return;
34811819Sjulian
34911819Sjuliandropwithreset:
350139932Srwatson	IPX_UNLOCK(ipxp);
351130822Srwatson	if (dropsocket) {
352130822Srwatson		struct socket *head;
353130822Srwatson		ACCEPT_LOCK();
354130822Srwatson		KASSERT((so->so_qstate & SQ_INCOMP) != 0,
355130822Srwatson		    ("spx_input: nascent socket not SQ_INCOMP on soabort()"));
356130822Srwatson		head = so->so_head;
357130822Srwatson		TAILQ_REMOVE(&head->so_incomp, so, so_list);
358130822Srwatson		head->so_incqlen--;
359130822Srwatson		so->so_qstate &= ~SQ_INCOMP;
360130822Srwatson		so->so_head = NULL;
361130822Srwatson		ACCEPT_UNLOCK();
36225652Sjhay		soabort(so);
363139931Srwatson		cb = NULL;
364130822Srwatson	}
365139932Srwatson	IPX_LIST_UNLOCK();
36611819Sjulian	si->si_seq = ntohs(si->si_seq);
36711819Sjulian	si->si_ack = ntohs(si->si_ack);
36811819Sjulian	si->si_alo = ntohs(si->si_alo);
36925652Sjhay	m_freem(dtom(si));
370139931Srwatson	if (cb == NULL || cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG ||
371139931Srwatson	    traceallspxs)
37211819Sjulian		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
37311819Sjulian	return;
37411819Sjulian
37511819Sjuliandrop:
37611819Sjulianbad:
377139580Srwatson	if (cb == NULL || cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG ||
37897658Stanimura            traceallspxs)
37911819Sjulian		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
380139932Srwatson	IPX_UNLOCK(ipxp);
381139932Srwatson	IPX_LIST_UNLOCK();
38211819Sjulian	m_freem(m);
38311819Sjulian}
38411819Sjulian
38511819Sjulian/*
38611819Sjulian * This is structurally similar to the tcp reassembly routine
38711819Sjulian * but its function is somewhat different:  It merely queues
38811819Sjulian * packets up, and suppresses duplicates.
38911819Sjulian */
39025652Sjhaystatic int
391157067Srwatsonspx_reass(struct spxpcb *cb, struct spx *si)
39211819Sjulian{
393157067Srwatson	struct spx_q *q;
394157067Srwatson	struct mbuf *m;
395157067Srwatson	struct socket *so = cb->s_ipxpcb->ipxp_socket;
39611819Sjulian	char packetp = cb->s_flags & SF_HI;
39711819Sjulian	int incr;
39811819Sjulian	char wakeup = 0;
39911819Sjulian
400139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
401139932Srwatson
40211819Sjulian	if (si == SI(0))
40311819Sjulian		goto present;
40411819Sjulian	/*
40511819Sjulian	 * Update our news from them.
40611819Sjulian	 */
40711819Sjulian	if (si->si_cc & SPX_SA)
40811819Sjulian		cb->s_flags |= (spx_use_delack ? SF_DELACK : SF_ACKNOW);
40911819Sjulian	if (SSEQ_GT(si->si_alo, cb->s_ralo))
41011819Sjulian		cb->s_flags |= SF_WIN;
41111819Sjulian	if (SSEQ_LEQ(si->si_ack, cb->s_rack)) {
41211819Sjulian		if ((si->si_cc & SPX_SP) && cb->s_rack != (cb->s_smax + 1)) {
41311819Sjulian			spxstat.spxs_rcvdupack++;
41411819Sjulian			/*
41511819Sjulian			 * If this is a completely duplicate ack
41611819Sjulian			 * and other conditions hold, we assume
41711819Sjulian			 * a packet has been dropped and retransmit
41811819Sjulian			 * it exactly as in tcp_input().
41911819Sjulian			 */
42011819Sjulian			if (si->si_ack != cb->s_rack ||
42111819Sjulian			    si->si_alo != cb->s_ralo)
42211819Sjulian				cb->s_dupacks = 0;
42311819Sjulian			else if (++cb->s_dupacks == spxrexmtthresh) {
42411819Sjulian				u_short onxt = cb->s_snxt;
42511819Sjulian				int cwnd = cb->s_cwnd;
42611819Sjulian
42711819Sjulian				cb->s_snxt = si->si_ack;
42811819Sjulian				cb->s_cwnd = CUNIT;
42911819Sjulian				cb->s_force = 1 + SPXT_REXMT;
430139579Srwatson				spx_output(cb, NULL);
43111819Sjulian				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
43211819Sjulian				cb->s_rtt = 0;
43311819Sjulian				if (cwnd >= 4 * CUNIT)
43411819Sjulian					cb->s_cwnd = cwnd / 2;
43511819Sjulian				if (SSEQ_GT(onxt, cb->s_snxt))
43611819Sjulian					cb->s_snxt = onxt;
43711819Sjulian				return (1);
43811819Sjulian			}
43911819Sjulian		} else
44011819Sjulian			cb->s_dupacks = 0;
44111819Sjulian		goto update_window;
44211819Sjulian	}
44311819Sjulian	cb->s_dupacks = 0;
44411819Sjulian	/*
44511819Sjulian	 * If our correspondent acknowledges data we haven't sent
44611819Sjulian	 * TCP would drop the packet after acking.  We'll be a little
44711819Sjulian	 * more permissive
44811819Sjulian	 */
44911819Sjulian	if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) {
45011819Sjulian		spxstat.spxs_rcvacktoomuch++;
45111819Sjulian		si->si_ack = cb->s_smax + 1;
45211819Sjulian	}
45311819Sjulian	spxstat.spxs_rcvackpack++;
45411819Sjulian	/*
45511819Sjulian	 * If transmit timer is running and timed sequence
45611819Sjulian	 * number was acked, update smoothed round trip time.
45711819Sjulian	 * See discussion of algorithm in tcp_input.c
45811819Sjulian	 */
45911819Sjulian	if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) {
46011819Sjulian		spxstat.spxs_rttupdated++;
46111819Sjulian		if (cb->s_srtt != 0) {
462157067Srwatson			short delta;
46311819Sjulian			delta = cb->s_rtt - (cb->s_srtt >> 3);
46411819Sjulian			if ((cb->s_srtt += delta) <= 0)
46511819Sjulian				cb->s_srtt = 1;
46611819Sjulian			if (delta < 0)
46711819Sjulian				delta = -delta;
46811819Sjulian			delta -= (cb->s_rttvar >> 2);
46911819Sjulian			if ((cb->s_rttvar += delta) <= 0)
47011819Sjulian				cb->s_rttvar = 1;
47111819Sjulian		} else {
47211819Sjulian			/*
47311819Sjulian			 * No rtt measurement yet
47411819Sjulian			 */
47511819Sjulian			cb->s_srtt = cb->s_rtt << 3;
47611819Sjulian			cb->s_rttvar = cb->s_rtt << 1;
47711819Sjulian		}
47811819Sjulian		cb->s_rtt = 0;
47911819Sjulian		cb->s_rxtshift = 0;
48011819Sjulian		SPXT_RANGESET(cb->s_rxtcur,
48111819Sjulian			((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
48211819Sjulian			SPXTV_MIN, SPXTV_REXMTMAX);
48311819Sjulian	}
48411819Sjulian	/*
48511819Sjulian	 * If all outstanding data is acked, stop retransmit
48611819Sjulian	 * timer and remember to restart (more output or persist).
48711819Sjulian	 * If there is more data to be acked, restart retransmit
48811819Sjulian	 * timer, using current (possibly backed-off) value;
48911819Sjulian	 */
49011819Sjulian	if (si->si_ack == cb->s_smax + 1) {
49111819Sjulian		cb->s_timer[SPXT_REXMT] = 0;
49211819Sjulian		cb->s_flags |= SF_RXT;
49311819Sjulian	} else if (cb->s_timer[SPXT_PERSIST] == 0)
49411819Sjulian		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
49511819Sjulian	/*
49611819Sjulian	 * When new data is acked, open the congestion window.
49711819Sjulian	 * If the window gives us less than ssthresh packets
49811819Sjulian	 * in flight, open exponentially (maxseg at a time).
49911819Sjulian	 * Otherwise open linearly (maxseg^2 / cwnd at a time).
50011819Sjulian	 */
50111819Sjulian	incr = CUNIT;
50211819Sjulian	if (cb->s_cwnd > cb->s_ssthresh)
50311819Sjulian		incr = max(incr * incr / cb->s_cwnd, 1);
50411819Sjulian	cb->s_cwnd = min(cb->s_cwnd + incr, cb->s_cwmx);
50511819Sjulian	/*
50611819Sjulian	 * Trim Acked data from output queue.
50711819Sjulian	 */
508139589Srwatson	SOCKBUF_LOCK(&so->so_snd);
50911819Sjulian	while ((m = so->so_snd.sb_mb) != NULL) {
51011819Sjulian		if (SSEQ_LT((mtod(m, struct spx *))->si_seq, si->si_ack))
511139589Srwatson			sbdroprecord_locked(&so->so_snd);
51211819Sjulian		else
51311819Sjulian			break;
51411819Sjulian	}
515139589Srwatson	sowwakeup_locked(so);
51611819Sjulian	cb->s_rack = si->si_ack;
51711819Sjulianupdate_window:
51811819Sjulian	if (SSEQ_LT(cb->s_snxt, cb->s_rack))
51911819Sjulian		cb->s_snxt = cb->s_rack;
52043311Sdillon	if (SSEQ_LT(cb->s_swl1, si->si_seq) || ((cb->s_swl1 == si->si_seq &&
52143311Sdillon	    (SSEQ_LT(cb->s_swl2, si->si_ack))) ||
52243305Sdillon	     (cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo)))) {
52311819Sjulian		/* keep track of pure window updates */
52411819Sjulian		if ((si->si_cc & SPX_SP) && cb->s_swl2 == si->si_ack
52511819Sjulian		    && SSEQ_LT(cb->s_ralo, si->si_alo)) {
52611819Sjulian			spxstat.spxs_rcvwinupd++;
52711819Sjulian			spxstat.spxs_rcvdupack--;
52811819Sjulian		}
52911819Sjulian		cb->s_ralo = si->si_alo;
53011819Sjulian		cb->s_swl1 = si->si_seq;
53111819Sjulian		cb->s_swl2 = si->si_ack;
53211819Sjulian		cb->s_swnd = (1 + si->si_alo - si->si_ack);
53311819Sjulian		if (cb->s_swnd > cb->s_smxw)
53411819Sjulian			cb->s_smxw = cb->s_swnd;
53511819Sjulian		cb->s_flags |= SF_WIN;
53611819Sjulian	}
53711819Sjulian	/*
53811819Sjulian	 * If this packet number is higher than that which
53911819Sjulian	 * we have allocated refuse it, unless urgent
54011819Sjulian	 */
54111819Sjulian	if (SSEQ_GT(si->si_seq, cb->s_alo)) {
54211819Sjulian		if (si->si_cc & SPX_SP) {
54311819Sjulian			spxstat.spxs_rcvwinprobe++;
54411819Sjulian			return (1);
54511819Sjulian		} else
54611819Sjulian			spxstat.spxs_rcvpackafterwin++;
54711819Sjulian		if (si->si_cc & SPX_OB) {
54811819Sjulian			if (SSEQ_GT(si->si_seq, cb->s_alo + 60)) {
54925652Sjhay				m_freem(dtom(si));
55011819Sjulian				return (0);
55111819Sjulian			} /* else queue this packet; */
55211819Sjulian		} else {
553139579Srwatson#ifdef BROKEN
554139579Srwatson			/*
555139579Srwatson			 * XXXRW: This is broken on at least one count:
556139579Srwatson			 * spx_close() will free the ipxp and related parts,
557139579Srwatson			 * which are then touched by spx_input() after the
558139579Srwatson			 * return from spx_reass().
559139579Srwatson			 */
560157067Srwatson			/*struct socket *so = cb->s_ipxpcb->ipxp_socket;
56111819Sjulian			if (so->so_state && SS_NOFDREF) {
56225652Sjhay				spx_close(cb);
56397658Stanimura			} else
56497658Stanimura				       would crash system*/
565139579Srwatson#endif
56611819Sjulian			spx_istat.notyet++;
56725652Sjhay			m_freem(dtom(si));
56811819Sjulian			return (0);
56911819Sjulian		}
57011819Sjulian	}
57111819Sjulian	/*
57211819Sjulian	 * If this is a system packet, we don't need to
57311819Sjulian	 * queue it up, and won't update acknowledge #
57411819Sjulian	 */
57511819Sjulian	if (si->si_cc & SPX_SP) {
57611819Sjulian		return (1);
57711819Sjulian	}
57811819Sjulian	/*
57911819Sjulian	 * We have already seen this packet, so drop.
58011819Sjulian	 */
58111819Sjulian	if (SSEQ_LT(si->si_seq, cb->s_ack)) {
58211819Sjulian		spx_istat.bdreas++;
58311819Sjulian		spxstat.spxs_rcvduppack++;
58411819Sjulian		if (si->si_seq == cb->s_ack - 1)
58511819Sjulian			spx_istat.lstdup++;
58611819Sjulian		return (1);
58711819Sjulian	}
58811819Sjulian	/*
58911819Sjulian	 * Loop through all packets queued up to insert in
59011819Sjulian	 * appropriate sequence.
59111819Sjulian	 */
59225652Sjhay	for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) {
59311819Sjulian		if (si->si_seq == SI(q)->si_seq) {
59411819Sjulian			spxstat.spxs_rcvduppack++;
59511819Sjulian			return (1);
59611819Sjulian		}
59711819Sjulian		if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) {
59811819Sjulian			spxstat.spxs_rcvoopack++;
59911819Sjulian			break;
60011819Sjulian		}
60111819Sjulian	}
60211819Sjulian	insque(si, q->si_prev);
60311819Sjulian	/*
60411819Sjulian	 * If this packet is urgent, inform process
60511819Sjulian	 */
60611819Sjulian	if (si->si_cc & SPX_OB) {
60711819Sjulian		cb->s_iobc = ((char *)si)[1 + sizeof(*si)];
60811819Sjulian		sohasoutofband(so);
60911819Sjulian		cb->s_oobflags |= SF_IOOB;
61011819Sjulian	}
61111819Sjulianpresent:
61211819Sjulian#define SPINC sizeof(struct spxhdr)
613139590Srwatson	SOCKBUF_LOCK(&so->so_rcv);
61411819Sjulian	/*
61511819Sjulian	 * Loop through all packets queued up to update acknowledge
61611819Sjulian	 * number, and present all acknowledged data to user;
61711819Sjulian	 * If in packet interface mode, show packet headers.
61811819Sjulian	 */
61925652Sjhay	for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) {
62011819Sjulian		  if (SI(q)->si_seq == cb->s_ack) {
62111819Sjulian			cb->s_ack++;
62211819Sjulian			m = dtom(q);
62311819Sjulian			if (SI(q)->si_cc & SPX_OB) {
62411819Sjulian				cb->s_oobflags &= ~SF_IOOB;
62511819Sjulian				if (so->so_rcv.sb_cc)
62611819Sjulian					so->so_oobmark = so->so_rcv.sb_cc;
627131031Srwatson				else
628130480Srwatson					so->so_rcv.sb_state |= SBS_RCVATMARK;
62911819Sjulian			}
63011819Sjulian			q = q->si_prev;
63111819Sjulian			remque(q->si_next);
63211819Sjulian			wakeup = 1;
63311819Sjulian			spxstat.spxs_rcvpack++;
63411819Sjulian#ifdef SF_NEWCALL
63511819Sjulian			if (cb->s_flags2 & SF_NEWCALL) {
63611819Sjulian				struct spxhdr *sp = mtod(m, struct spxhdr *);
63711819Sjulian				u_char dt = sp->spx_dt;
63811819Sjulian				spx_newchecks[4]++;
63911819Sjulian				if (dt != cb->s_rhdr.spx_dt) {
64011819Sjulian					struct mbuf *mm =
641111119Simp					   m_getclr(M_DONTWAIT, MT_CONTROL);
64211819Sjulian					spx_newchecks[0]++;
64311819Sjulian					if (mm != NULL) {
64411819Sjulian						u_short *s =
64511819Sjulian							mtod(mm, u_short *);
64611819Sjulian						cb->s_rhdr.spx_dt = dt;
64711819Sjulian						mm->m_len = 5; /*XXX*/
64811819Sjulian						s[0] = 5;
64911819Sjulian						s[1] = 1;
65011819Sjulian						*(u_char *)(&s[2]) = dt;
651139590Srwatson						sbappend_locked(&so->so_rcv, mm);
65211819Sjulian					}
65311819Sjulian				}
65411819Sjulian				if (sp->spx_cc & SPX_OB) {
65511819Sjulian					MCHTYPE(m, MT_OOBDATA);
65611819Sjulian					spx_newchecks[1]++;
65711819Sjulian					so->so_oobmark = 0;
658130480Srwatson					so->so_rcv.sb_state &= ~SBS_RCVATMARK;
65911819Sjulian				}
66011819Sjulian				if (packetp == 0) {
66111819Sjulian					m->m_data += SPINC;
66211819Sjulian					m->m_len -= SPINC;
66311819Sjulian					m->m_pkthdr.len -= SPINC;
66411819Sjulian				}
66511819Sjulian				if ((sp->spx_cc & SPX_EM) || packetp) {
666139590Srwatson					sbappendrecord_locked(&so->so_rcv, m);
66711819Sjulian					spx_newchecks[9]++;
66811819Sjulian				} else
669139590Srwatson					sbappend_locked(&so->so_rcv, m);
67011819Sjulian			} else
67111819Sjulian#endif
67211819Sjulian			if (packetp) {
673139590Srwatson				sbappendrecord_locked(&so->so_rcv, m);
67411819Sjulian			} else {
67511819Sjulian				cb->s_rhdr = *mtod(m, struct spxhdr *);
67611819Sjulian				m->m_data += SPINC;
67711819Sjulian				m->m_len -= SPINC;
67811819Sjulian				m->m_pkthdr.len -= SPINC;
679139590Srwatson				sbappend_locked(&so->so_rcv, m);
68011819Sjulian			}
68111819Sjulian		  } else
68211819Sjulian			break;
68311819Sjulian	}
68497658Stanimura	if (wakeup)
685139590Srwatson		sorwakeup_locked(so);
686139590Srwatson	else
687139590Srwatson		SOCKBUF_UNLOCK(&so->so_rcv);
68811819Sjulian	return (0);
68911819Sjulian}
69011819Sjulian
69111819Sjulianvoid
692157067Srwatsonspx_ctlinput(int cmd, struct sockaddr *arg_as_sa, void *dummy)
69311819Sjulian{
69411819Sjulian
695157050Srwatson	/* Currently, nothing. */
69611819Sjulian}
69711819Sjulian
69825652Sjhaystatic int
699157067Srwatsonspx_output(struct spxpcb *cb, struct mbuf *m0)
70011819Sjulian{
70111819Sjulian	struct socket *so = cb->s_ipxpcb->ipxp_socket;
702157067Srwatson	struct mbuf *m;
703157067Srwatson	struct spx *si = NULL;
704157067Srwatson	struct sockbuf *sb = &so->so_snd;
70511819Sjulian	int len = 0, win, rcv_win;
70611819Sjulian	short span, off, recordp = 0;
70711819Sjulian	u_short alo;
70811819Sjulian	int error = 0, sendalot;
70911819Sjulian#ifdef notdef
71011819Sjulian	int idle;
71111819Sjulian#endif
71211819Sjulian	struct mbuf *mprev;
71311819Sjulian
714139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
715139932Srwatson
71625652Sjhay	if (m0 != NULL) {
71711819Sjulian		int mtu = cb->s_mtu;
71811819Sjulian		int datalen;
71911819Sjulian		/*
72011819Sjulian		 * Make sure that packet isn't too big.
72111819Sjulian		 */
72225652Sjhay		for (m = m0; m != NULL; m = m->m_next) {
72311819Sjulian			mprev = m;
72411819Sjulian			len += m->m_len;
72511819Sjulian			if (m->m_flags & M_EOR)
72611819Sjulian				recordp = 1;
72711819Sjulian		}
72811819Sjulian		datalen = (cb->s_flags & SF_HO) ?
72925652Sjhay				len - sizeof(struct spxhdr) : len;
73011819Sjulian		if (datalen > mtu) {
73111819Sjulian			if (cb->s_flags & SF_PI) {
73211819Sjulian				m_freem(m0);
73311819Sjulian				return (EMSGSIZE);
73411819Sjulian			} else {
73511819Sjulian				int oldEM = cb->s_cc & SPX_EM;
73611819Sjulian
73711819Sjulian				cb->s_cc &= ~SPX_EM;
73811819Sjulian				while (len > mtu) {
73911819Sjulian					/*
74011819Sjulian					 * Here we are only being called
74111819Sjulian					 * from usrreq(), so it is OK to
74211819Sjulian					 * block.
74311819Sjulian					 */
744111119Simp					m = m_copym(m0, 0, mtu, M_TRYWAIT);
74511819Sjulian					if (cb->s_flags & SF_NEWCALL) {
74611819Sjulian					    struct mbuf *mm = m;
74711819Sjulian					    spx_newchecks[7]++;
74825652Sjhay					    while (mm != NULL) {
74911819Sjulian						mm->m_flags &= ~M_EOR;
75011819Sjulian						mm = mm->m_next;
75111819Sjulian					    }
75211819Sjulian					}
75311819Sjulian					error = spx_output(cb, m);
75411819Sjulian					if (error) {
75511819Sjulian						cb->s_cc |= oldEM;
75611819Sjulian						m_freem(m0);
75725652Sjhay						return (error);
75811819Sjulian					}
75911819Sjulian					m_adj(m0, mtu);
76011819Sjulian					len -= mtu;
76111819Sjulian				}
76211819Sjulian				cb->s_cc |= oldEM;
76311819Sjulian			}
76411819Sjulian		}
76511819Sjulian		/*
76611819Sjulian		 * Force length even, by adding a "garbage byte" if
76711819Sjulian		 * necessary.
76811819Sjulian		 */
76911819Sjulian		if (len & 1) {
77011819Sjulian			m = mprev;
77111819Sjulian			if (M_TRAILINGSPACE(m) >= 1)
77211819Sjulian				m->m_len++;
77311819Sjulian			else {
774111119Simp				struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
77511819Sjulian
77625652Sjhay				if (m1 == NULL) {
77711819Sjulian					m_freem(m0);
77811819Sjulian					return (ENOBUFS);
77911819Sjulian				}
78011819Sjulian				m1->m_len = 1;
78111819Sjulian				*(mtod(m1, u_char *)) = 0;
78211819Sjulian				m->m_next = m1;
78311819Sjulian			}
78411819Sjulian		}
785151967Sandre		m = m_gethdr(M_DONTWAIT, MT_DATA);
78625652Sjhay		if (m == NULL) {
78711819Sjulian			m_freem(m0);
78811819Sjulian			return (ENOBUFS);
78911819Sjulian		}
79011819Sjulian		/*
79111819Sjulian		 * Fill in mbuf with extended SP header
79211819Sjulian		 * and addresses and length put into network format.
79311819Sjulian		 */
79425652Sjhay		MH_ALIGN(m, sizeof(struct spx));
79525652Sjhay		m->m_len = sizeof(struct spx);
79611819Sjulian		m->m_next = m0;
79711819Sjulian		si = mtod(m, struct spx *);
79811819Sjulian		si->si_i = *cb->s_ipx;
79911819Sjulian		si->si_s = cb->s_shdr;
80011819Sjulian		if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) {
801157067Srwatson			struct spxhdr *sh;
80225652Sjhay			if (m0->m_len < sizeof(*sh)) {
80311819Sjulian				if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) {
80425652Sjhay					m_free(m);
80511819Sjulian					m_freem(m0);
80611819Sjulian					return (EINVAL);
80711819Sjulian				}
80811819Sjulian				m->m_next = m0;
80911819Sjulian			}
81011819Sjulian			sh = mtod(m0, struct spxhdr *);
81111819Sjulian			si->si_dt = sh->spx_dt;
81211819Sjulian			si->si_cc |= sh->spx_cc & SPX_EM;
81325652Sjhay			m0->m_len -= sizeof(*sh);
81425652Sjhay			m0->m_data += sizeof(*sh);
81525652Sjhay			len -= sizeof(*sh);
81611819Sjulian		}
81711819Sjulian		len += sizeof(*si);
81811819Sjulian		if ((cb->s_flags2 & SF_NEWCALL) && recordp) {
81925652Sjhay			si->si_cc |= SPX_EM;
82011819Sjulian			spx_newchecks[8]++;
82111819Sjulian		}
82211819Sjulian		if (cb->s_oobflags & SF_SOOB) {
82311819Sjulian			/*
82411819Sjulian			 * Per jqj@cornell:
82511819Sjulian			 * make sure OB packets convey exactly 1 byte.
82611819Sjulian			 * If the packet is 1 byte or larger, we
82711819Sjulian			 * have already guaranted there to be at least
82811819Sjulian			 * one garbage byte for the checksum, and
82911819Sjulian			 * extra bytes shouldn't hurt!
83011819Sjulian			 */
83111819Sjulian			if (len > sizeof(*si)) {
83211819Sjulian				si->si_cc |= SPX_OB;
83311819Sjulian				len = (1 + sizeof(*si));
83411819Sjulian			}
83511819Sjulian		}
83611819Sjulian		si->si_len = htons((u_short)len);
83711819Sjulian		m->m_pkthdr.len = ((len - 1) | 1) + 1;
83811819Sjulian		/*
83911819Sjulian		 * queue stuff up for output
84011819Sjulian		 */
84111819Sjulian		sbappendrecord(sb, m);
84211819Sjulian		cb->s_seq++;
84311819Sjulian	}
84411819Sjulian#ifdef notdef
84511819Sjulian	idle = (cb->s_smax == (cb->s_rack - 1));
84611819Sjulian#endif
84711819Sjulianagain:
84811819Sjulian	sendalot = 0;
84911819Sjulian	off = cb->s_snxt - cb->s_rack;
85025652Sjhay	win = min(cb->s_swnd, (cb->s_cwnd / CUNIT));
85111819Sjulian
85211819Sjulian	/*
85311819Sjulian	 * If in persist timeout with window of 0, send a probe.
85411819Sjulian	 * Otherwise, if window is small but nonzero
85511819Sjulian	 * and timer expired, send what we can and go into
85611819Sjulian	 * transmit state.
85711819Sjulian	 */
85811819Sjulian	if (cb->s_force == 1 + SPXT_PERSIST) {
85911819Sjulian		if (win != 0) {
86011819Sjulian			cb->s_timer[SPXT_PERSIST] = 0;
86111819Sjulian			cb->s_rxtshift = 0;
86211819Sjulian		}
86311819Sjulian	}
86411819Sjulian	span = cb->s_seq - cb->s_rack;
86511819Sjulian	len = min(span, win) - off;
86611819Sjulian
86711819Sjulian	if (len < 0) {
86811819Sjulian		/*
86911819Sjulian		 * Window shrank after we went into it.
87011819Sjulian		 * If window shrank to 0, cancel pending
87111819Sjulian		 * restransmission and pull s_snxt back
87211819Sjulian		 * to (closed) window.  We will enter persist
87311819Sjulian		 * state below.  If the widndow didn't close completely,
87411819Sjulian		 * just wait for an ACK.
87511819Sjulian		 */
87611819Sjulian		len = 0;
87711819Sjulian		if (win == 0) {
87811819Sjulian			cb->s_timer[SPXT_REXMT] = 0;
87911819Sjulian			cb->s_snxt = cb->s_rack;
88011819Sjulian		}
88111819Sjulian	}
88211819Sjulian	if (len > 1)
88311819Sjulian		sendalot = 1;
88411819Sjulian	rcv_win = sbspace(&so->so_rcv);
88511819Sjulian
88611819Sjulian	/*
88711819Sjulian	 * Send if we owe peer an ACK.
88811819Sjulian	 */
88911819Sjulian	if (cb->s_oobflags & SF_SOOB) {
89011819Sjulian		/*
89111819Sjulian		 * must transmit this out of band packet
89211819Sjulian		 */
89311819Sjulian		cb->s_oobflags &= ~ SF_SOOB;
89411819Sjulian		sendalot = 1;
89511819Sjulian		spxstat.spxs_sndurg++;
89611819Sjulian		goto found;
89711819Sjulian	}
89811819Sjulian	if (cb->s_flags & SF_ACKNOW)
89911819Sjulian		goto send;
90011819Sjulian	if (cb->s_state < TCPS_ESTABLISHED)
90111819Sjulian		goto send;
90211819Sjulian	/*
90311819Sjulian	 * Silly window can't happen in spx.
90411819Sjulian	 * Code from tcp deleted.
90511819Sjulian	 */
90611819Sjulian	if (len)
90711819Sjulian		goto send;
90811819Sjulian	/*
90911819Sjulian	 * Compare available window to amount of window
91011819Sjulian	 * known to peer (as advertised window less
91111819Sjulian	 * next expected input.)  If the difference is at least two
91211819Sjulian	 * packets or at least 35% of the mximum possible window,
91311819Sjulian	 * then want to send a window update to peer.
91411819Sjulian	 */
91511819Sjulian	if (rcv_win > 0) {
91611819Sjulian		u_short delta =  1 + cb->s_alo - cb->s_ack;
91711819Sjulian		int adv = rcv_win - (delta * cb->s_mtu);
918139584Srwatson
91911819Sjulian		if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) ||
92011819Sjulian		    (100 * adv / so->so_rcv.sb_hiwat >= 35)) {
92111819Sjulian			spxstat.spxs_sndwinup++;
92211819Sjulian			cb->s_flags |= SF_ACKNOW;
92311819Sjulian			goto send;
92411819Sjulian		}
92511819Sjulian
92611819Sjulian	}
92711819Sjulian	/*
92811819Sjulian	 * Many comments from tcp_output.c are appropriate here
92911819Sjulian	 * including . . .
93011819Sjulian	 * If send window is too small, there is data to transmit, and no
93111819Sjulian	 * retransmit or persist is pending, then go to persist state.
93211819Sjulian	 * If nothing happens soon, send when timer expires:
93311819Sjulian	 * if window is nonzero, transmit what we can,
93411819Sjulian	 * otherwise send a probe.
93511819Sjulian	 */
93611819Sjulian	if (so->so_snd.sb_cc && cb->s_timer[SPXT_REXMT] == 0 &&
93711819Sjulian		cb->s_timer[SPXT_PERSIST] == 0) {
93811819Sjulian			cb->s_rxtshift = 0;
93911819Sjulian			spx_setpersist(cb);
94011819Sjulian	}
94111819Sjulian	/*
94211819Sjulian	 * No reason to send a packet, just return.
94311819Sjulian	 */
94411819Sjulian	cb->s_outx = 1;
94511819Sjulian	return (0);
94611819Sjulian
94711819Sjuliansend:
94811819Sjulian	/*
94911819Sjulian	 * Find requested packet.
95011819Sjulian	 */
95111819Sjulian	si = 0;
95211819Sjulian	if (len > 0) {
95311819Sjulian		cb->s_want = cb->s_snxt;
95425652Sjhay		for (m = sb->sb_mb; m != NULL; m = m->m_act) {
95511819Sjulian			si = mtod(m, struct spx *);
95611819Sjulian			if (SSEQ_LEQ(cb->s_snxt, si->si_seq))
95711819Sjulian				break;
95811819Sjulian		}
95911819Sjulian	found:
96025652Sjhay		if (si != NULL) {
96111819Sjulian			if (si->si_seq == cb->s_snxt)
96211819Sjulian					cb->s_snxt++;
96311819Sjulian				else
96411819Sjulian					spxstat.spxs_sndvoid++, si = 0;
96511819Sjulian		}
96611819Sjulian	}
96711819Sjulian	/*
96811819Sjulian	 * update window
96911819Sjulian	 */
97011819Sjulian	if (rcv_win < 0)
97111819Sjulian		rcv_win = 0;
97211819Sjulian	alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu));
973139584Srwatson	if (SSEQ_LT(alo, cb->s_alo))
97411819Sjulian		alo = cb->s_alo;
97511819Sjulian
97625652Sjhay	if (si != NULL) {
97711819Sjulian		/*
97811819Sjulian		 * must make a copy of this packet for
97911819Sjulian		 * ipx_output to monkey with
98011819Sjulian		 */
98111819Sjulian		m = m_copy(dtom(si), 0, (int)M_COPYALL);
98211819Sjulian		if (m == NULL) {
98311819Sjulian			return (ENOBUFS);
98411819Sjulian		}
98511819Sjulian		si = mtod(m, struct spx *);
98611819Sjulian		if (SSEQ_LT(si->si_seq, cb->s_smax))
98711819Sjulian			spxstat.spxs_sndrexmitpack++;
98811819Sjulian		else
98911819Sjulian			spxstat.spxs_sndpack++;
99011819Sjulian	} else if (cb->s_force || cb->s_flags & SF_ACKNOW) {
99111819Sjulian		/*
99211819Sjulian		 * Must send an acknowledgement or a probe
99311819Sjulian		 */
99411819Sjulian		if (cb->s_force)
99511819Sjulian			spxstat.spxs_sndprobe++;
99611819Sjulian		if (cb->s_flags & SF_ACKNOW)
99711819Sjulian			spxstat.spxs_sndacks++;
998151967Sandre		m = m_gethdr(M_DONTWAIT, MT_DATA);
99925652Sjhay		if (m == NULL)
100011819Sjulian			return (ENOBUFS);
100111819Sjulian		/*
100211819Sjulian		 * Fill in mbuf with extended SP header
100311819Sjulian		 * and addresses and length put into network format.
100411819Sjulian		 */
100525652Sjhay		MH_ALIGN(m, sizeof(struct spx));
100625652Sjhay		m->m_len = sizeof(*si);
100725652Sjhay		m->m_pkthdr.len = sizeof(*si);
100811819Sjulian		si = mtod(m, struct spx *);
100911819Sjulian		si->si_i = *cb->s_ipx;
101011819Sjulian		si->si_s = cb->s_shdr;
101111819Sjulian		si->si_seq = cb->s_smax + 1;
101225652Sjhay		si->si_len = htons(sizeof(*si));
101311819Sjulian		si->si_cc |= SPX_SP;
101411819Sjulian	} else {
101511819Sjulian		cb->s_outx = 3;
101697658Stanimura		if (so->so_options & SO_DEBUG || traceallspxs)
101711819Sjulian			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
101811819Sjulian		return (0);
101911819Sjulian	}
102011819Sjulian	/*
102111819Sjulian	 * Stuff checksum and output datagram.
102211819Sjulian	 */
102311819Sjulian	if ((si->si_cc & SPX_SP) == 0) {
102411819Sjulian		if (cb->s_force != (1 + SPXT_PERSIST) ||
102511819Sjulian		    cb->s_timer[SPXT_PERSIST] == 0) {
102611819Sjulian			/*
1027139584Srwatson			 * If this is a new packet and we are not currently
102811819Sjulian			 * timing anything, time this one.
102911819Sjulian			 */
103011819Sjulian			if (SSEQ_LT(cb->s_smax, si->si_seq)) {
103111819Sjulian				cb->s_smax = si->si_seq;
103211819Sjulian				if (cb->s_rtt == 0) {
103311819Sjulian					spxstat.spxs_segstimed++;
103411819Sjulian					cb->s_rtseq = si->si_seq;
103511819Sjulian					cb->s_rtt = 1;
103611819Sjulian				}
103711819Sjulian			}
103811819Sjulian			/*
103911819Sjulian			 * Set rexmt timer if not currently set,
104011819Sjulian			 * Initial value for retransmit timer is smoothed
104111819Sjulian			 * round-trip time + 2 * round-trip time variance.
104211819Sjulian			 * Initialize shift counter which is used for backoff
104311819Sjulian			 * of retransmit time.
104411819Sjulian			 */
104511819Sjulian			if (cb->s_timer[SPXT_REXMT] == 0 &&
104611819Sjulian			    cb->s_snxt != cb->s_rack) {
104711819Sjulian				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
104811819Sjulian				if (cb->s_timer[SPXT_PERSIST]) {
104911819Sjulian					cb->s_timer[SPXT_PERSIST] = 0;
105011819Sjulian					cb->s_rxtshift = 0;
105111819Sjulian				}
105211819Sjulian			}
105311819Sjulian		} else if (SSEQ_LT(cb->s_smax, si->si_seq)) {
105411819Sjulian			cb->s_smax = si->si_seq;
105511819Sjulian		}
105611819Sjulian	} else if (cb->s_state < TCPS_ESTABLISHED) {
105711819Sjulian		if (cb->s_rtt == 0)
105811819Sjulian			cb->s_rtt = 1; /* Time initial handshake */
105911819Sjulian		if (cb->s_timer[SPXT_REXMT] == 0)
106011819Sjulian			cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
106111819Sjulian	}
106211819Sjulian	{
106311819Sjulian		/*
106411819Sjulian		 * Do not request acks when we ack their data packets or
106511819Sjulian		 * when we do a gratuitous window update.
106611819Sjulian		 */
106711819Sjulian		if (((si->si_cc & SPX_SP) == 0) || cb->s_force)
106811819Sjulian				si->si_cc |= SPX_SA;
106911819Sjulian		si->si_seq = htons(si->si_seq);
107011819Sjulian		si->si_alo = htons(alo);
107111819Sjulian		si->si_ack = htons(cb->s_ack);
107211819Sjulian
107311819Sjulian		if (ipxcksum) {
107450519Sjhay			si->si_sum = ipx_cksum(m, ntohs(si->si_len));
107511819Sjulian		} else
107611819Sjulian			si->si_sum = 0xffff;
107711819Sjulian
107811819Sjulian		cb->s_outx = 4;
107997658Stanimura		if (so->so_options & SO_DEBUG || traceallspxs)
108011819Sjulian			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
108111819Sjulian
108297658Stanimura		if (so->so_options & SO_DONTROUTE)
1083139579Srwatson			error = ipx_outputfl(m, NULL, IPX_ROUTETOIF);
108497658Stanimura		else
108511819Sjulian			error = ipx_outputfl(m, &cb->s_ipxpcb->ipxp_route, 0);
108611819Sjulian	}
108711819Sjulian	if (error) {
108811819Sjulian		return (error);
108911819Sjulian	}
109011819Sjulian	spxstat.spxs_sndtotal++;
109111819Sjulian	/*
109211819Sjulian	 * Data sent (as far as we can tell).
109311819Sjulian	 * If this advertises a larger window than any other segment,
109411819Sjulian	 * then remember the size of the advertized window.
109511819Sjulian	 * Any pending ACK has now been sent.
109611819Sjulian	 */
109711819Sjulian	cb->s_force = 0;
109811819Sjulian	cb->s_flags &= ~(SF_ACKNOW|SF_DELACK);
109911819Sjulian	if (SSEQ_GT(alo, cb->s_alo))
110011819Sjulian		cb->s_alo = alo;
110111819Sjulian	if (sendalot)
110211819Sjulian		goto again;
110311819Sjulian	cb->s_outx = 5;
110411819Sjulian	return (0);
110511819Sjulian}
110611819Sjulian
110733181Seivindstatic int spx_do_persist_panics = 0;
110811819Sjulian
110925652Sjhaystatic void
1110157067Srwatsonspx_setpersist(struct spxpcb *cb)
111111819Sjulian{
1112157067Srwatson	int t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
111311819Sjulian
1114139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1115139932Srwatson
111611819Sjulian	if (cb->s_timer[SPXT_REXMT] && spx_do_persist_panics)
111711819Sjulian		panic("spx_output REXMT");
111811819Sjulian	/*
111911819Sjulian	 * Start/restart persistance timer.
112011819Sjulian	 */
112111819Sjulian	SPXT_RANGESET(cb->s_timer[SPXT_PERSIST],
112211819Sjulian	    t*spx_backoff[cb->s_rxtshift],
112311819Sjulian	    SPXTV_PERSMIN, SPXTV_PERSMAX);
112411819Sjulian	if (cb->s_rxtshift < SPX_MAXRXTSHIFT)
112511819Sjulian		cb->s_rxtshift++;
112611819Sjulian}
112725652Sjhay
112811819Sjulianint
1129157067Srwatsonspx_ctloutput(struct socket *so, struct sockopt *sopt)
113011819Sjulian{
113111819Sjulian	struct ipxpcb *ipxp = sotoipxpcb(so);
1132157067Srwatson	struct spxpcb *cb;
113338482Swollman	int mask, error;
113438482Swollman	short soptval;
113538482Swollman	u_short usoptval;
113638482Swollman	int optval;
113711819Sjulian
113838482Swollman	error = 0;
113938482Swollman
114038482Swollman	if (sopt->sopt_level != IPXPROTO_SPX) {
114111819Sjulian		/* This will have to be changed when we do more general
114211819Sjulian		   stacking of protocols */
114338482Swollman		return (ipx_ctloutput(so, sopt));
114411819Sjulian	}
114538482Swollman	if (ipxp == NULL)
114638482Swollman		return (EINVAL);
114738482Swollman	else
114811819Sjulian		cb = ipxtospxpcb(ipxp);
114911819Sjulian
115038482Swollman	switch (sopt->sopt_dir) {
115138482Swollman	case SOPT_GET:
115238482Swollman		switch (sopt->sopt_name) {
115311819Sjulian		case SO_HEADERS_ON_INPUT:
115411819Sjulian			mask = SF_HI;
115511819Sjulian			goto get_flags;
115611819Sjulian
115711819Sjulian		case SO_HEADERS_ON_OUTPUT:
115811819Sjulian			mask = SF_HO;
115911819Sjulian		get_flags:
1160139932Srwatson			/* Unlocked read. */
116138482Swollman			soptval = cb->s_flags & mask;
116238482Swollman			error = sooptcopyout(sopt, &soptval, sizeof soptval);
116311819Sjulian			break;
116411819Sjulian
116511819Sjulian		case SO_MTU:
1166139932Srwatson			/* Unlocked read. */
116738482Swollman			usoptval = cb->s_mtu;
116838482Swollman			error = sooptcopyout(sopt, &usoptval, sizeof usoptval);
116911819Sjulian			break;
117011819Sjulian
117111819Sjulian		case SO_LAST_HEADER:
1172139932Srwatson			/* Unlocked read. */
1173139584Srwatson			error = sooptcopyout(sopt, &cb->s_rhdr,
117438482Swollman					     sizeof cb->s_rhdr);
117511819Sjulian			break;
117611819Sjulian
117711819Sjulian		case SO_DEFAULT_HEADERS:
1178139932Srwatson			/* Unlocked read. */
1179139584Srwatson			error = sooptcopyout(sopt, &cb->s_shdr,
118038482Swollman					     sizeof cb->s_shdr);
118111819Sjulian			break;
118211819Sjulian
118311819Sjulian		default:
118438482Swollman			error = ENOPROTOOPT;
118511819Sjulian		}
118611819Sjulian		break;
118711819Sjulian
118838482Swollman	case SOPT_SET:
118938482Swollman		switch (sopt->sopt_name) {
119038482Swollman			/* XXX why are these shorts on get and ints on set?
119138482Swollman			   that doesn't make any sense... */
119211819Sjulian		case SO_HEADERS_ON_INPUT:
119311819Sjulian			mask = SF_HI;
119411819Sjulian			goto set_head;
119511819Sjulian
119611819Sjulian		case SO_HEADERS_ON_OUTPUT:
119711819Sjulian			mask = SF_HO;
119811819Sjulian		set_head:
119938482Swollman			error = sooptcopyin(sopt, &optval, sizeof optval,
120038482Swollman					    sizeof optval);
120138482Swollman			if (error)
120238482Swollman				break;
120338482Swollman
1204139932Srwatson			IPX_LOCK(ipxp);
120511819Sjulian			if (cb->s_flags & SF_PI) {
120638482Swollman				if (optval)
120711819Sjulian					cb->s_flags |= mask;
120811819Sjulian				else
120911819Sjulian					cb->s_flags &= ~mask;
121011819Sjulian			} else error = EINVAL;
1211139932Srwatson			IPX_UNLOCK(ipxp);
121211819Sjulian			break;
121311819Sjulian
121411819Sjulian		case SO_MTU:
121538482Swollman			error = sooptcopyin(sopt, &usoptval, sizeof usoptval,
121638482Swollman					    sizeof usoptval);
121738482Swollman			if (error)
121838482Swollman				break;
1219139932Srwatson			/* Unlocked write. */
122038482Swollman			cb->s_mtu = usoptval;
122111819Sjulian			break;
122211819Sjulian
122311819Sjulian#ifdef SF_NEWCALL
122411819Sjulian		case SO_NEWCALL:
122538482Swollman			error = sooptcopyin(sopt, &optval, sizeof optval,
122638482Swollman					    sizeof optval);
122738482Swollman			if (error)
122838482Swollman				break;
1229139932Srwatson			IPX_LOCK(ipxp);
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			}
1237139932Srwatson			IPX_UNLOCK(ipxp);
123811819Sjulian			break;
123911819Sjulian#endif
124011819Sjulian
124111819Sjulian		case SO_DEFAULT_HEADERS:
124211819Sjulian			{
124338482Swollman				struct spxhdr sp;
124438482Swollman
124538482Swollman				error = sooptcopyin(sopt, &sp, sizeof sp,
124638482Swollman						    sizeof sp);
124738482Swollman				if (error)
124838482Swollman					break;
1249139932Srwatson				IPX_LOCK(ipxp);
125038482Swollman				cb->s_dt = sp.spx_dt;
125138482Swollman				cb->s_cc = sp.spx_cc & SPX_EM;
1252139932Srwatson				IPX_UNLOCK(ipxp);
125311819Sjulian			}
125411819Sjulian			break;
125511819Sjulian
125611819Sjulian		default:
125738482Swollman			error = ENOPROTOOPT;
125811819Sjulian		}
125911819Sjulian		break;
126011819Sjulian	}
126138482Swollman	return (error);
126211819Sjulian}
126311819Sjulian
126424659Sjhaystatic int
1265157067Srwatsonspx_usr_abort(struct socket *so)
126611819Sjulian{
126724659Sjhay	struct ipxpcb *ipxp;
126824659Sjhay	struct spxpcb *cb;
126911819Sjulian
127024659Sjhay	ipxp = sotoipxpcb(so);
127124659Sjhay	cb = ipxtospxpcb(ipxp);
127211819Sjulian
1273139932Srwatson	IPX_LIST_LOCK();
1274139932Srwatson	IPX_LOCK(ipxp);
127524659Sjhay	spx_drop(cb, ECONNABORTED);
1276139932Srwatson	IPX_LIST_UNLOCK();
127724659Sjhay	return (0);
127824659Sjhay}
127911819Sjulian
128024659Sjhay/*
128124659Sjhay * Accept a connection.  Essentially all the work is
128224659Sjhay * done at higher levels; just return the address
128324659Sjhay * of the peer, storing through addr.
128424659Sjhay */
128524659Sjhaystatic int
1286157067Srwatsonspx_accept(struct socket *so, 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;
1296139932Srwatson	IPX_LOCK(ipxp);
129724659Sjhay	sipx->sipx_addr = ipxp->ipxp_faddr;
1298139932Srwatson	IPX_UNLOCK(ipxp);
1299139932Srwatson	*nam = sodupsockaddr((struct sockaddr *)sipx, M_WAITOK);
130024659Sjhay	return (0);
130124659Sjhay}
130224659Sjhay
130324659Sjhaystatic int
1304157067Srwatsonspx_attach(struct socket *so, int proto, struct thread *td)
130524659Sjhay{
130624659Sjhay	struct ipxpcb *ipxp;
130724659Sjhay	struct spxpcb *cb;
130824659Sjhay	struct mbuf *mm;
130924659Sjhay	struct sockbuf *sb;
1310139932Srwatson	int error;
131124659Sjhay
131224659Sjhay	ipxp = sotoipxpcb(so);
131324659Sjhay	cb = ipxtospxpcb(ipxp);
131424659Sjhay
1315139932Srwatson	IPX_LIST_LOCK();
1316139444Srwatson	error = ipx_pcballoc(so, &ipxpcb_list, td);
131724659Sjhay	if (error)
131824659Sjhay		goto spx_attach_end;
131924659Sjhay	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
132024659Sjhay		error = soreserve(so, (u_long) 3072, (u_long) 3072);
132111819Sjulian		if (error)
132224659Sjhay			goto spx_attach_end;
132324659Sjhay	}
132424659Sjhay	ipxp = sotoipxpcb(so);
132511819Sjulian
132669781Sdwmalone	MALLOC(cb, struct spxpcb *, sizeof *cb, M_PCB, M_NOWAIT | M_ZERO);
132711819Sjulian
132828270Swollman	if (cb == NULL) {
132924659Sjhay		error = ENOBUFS;
133024659Sjhay		goto spx_attach_end;
133124659Sjhay	}
133243711Sjhay	sb = &so->so_snd;
133328270Swollman
1334151967Sandre	mm = m_getclr(M_DONTWAIT, MT_DATA);
133524659Sjhay	if (mm == NULL) {
133628270Swollman		FREE(cb, M_PCB);
133724659Sjhay		error = ENOBUFS;
133824659Sjhay		goto spx_attach_end;
133924659Sjhay	}
134024659Sjhay	cb->s_ipx = mtod(mm, struct ipx *);
134124659Sjhay	cb->s_state = TCPS_LISTEN;
134224659Sjhay	cb->s_smax = -1;
134324659Sjhay	cb->s_swl1 = -1;
134424659Sjhay	cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q;
134524659Sjhay	cb->s_ipxpcb = ipxp;
134625652Sjhay	cb->s_mtu = 576 - sizeof(struct spx);
134724659Sjhay	cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu;
134824659Sjhay	cb->s_ssthresh = cb->s_cwnd;
134925652Sjhay	cb->s_cwmx = sbspace(sb) * CUNIT / (2 * sizeof(struct spx));
135024659Sjhay	/* Above is recomputed when connecting to account
135124659Sjhay	   for changed buffering or mtu's */
135224659Sjhay	cb->s_rtt = SPXTV_SRTTBASE;
135324659Sjhay	cb->s_rttvar = SPXTV_SRTTDFLT << 2;
135424659Sjhay	SPXT_RANGESET(cb->s_rxtcur,
135524659Sjhay	    ((SPXTV_SRTTBASE >> 2) + (SPXTV_SRTTDFLT << 2)) >> 1,
135624659Sjhay	    SPXTV_MIN, SPXTV_REXMTMAX);
1357139584Srwatson	ipxp->ipxp_pcb = (caddr_t)cb;
135824659Sjhayspx_attach_end:
1359139932Srwatson	IPX_LIST_UNLOCK();
136024659Sjhay	return (error);
136124659Sjhay}
136211819Sjulian
136324659Sjhaystatic int
1364157067Srwatsonspx_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
1365139584Srwatson{
136624659Sjhay	struct ipxpcb *ipxp;
1367139932Srwatson	int error;
136811819Sjulian
136924659Sjhay	ipxp = sotoipxpcb(so);
137011819Sjulian
1371139932Srwatson	IPX_LIST_LOCK();
1372139932Srwatson	IPX_LOCK(ipxp);
1373139932Srwatson	error = ipx_pcbbind(ipxp, nam, td);
1374139932Srwatson	IPX_UNLOCK(ipxp);
1375139932Srwatson	IPX_LIST_UNLOCK();
1376139932Srwatson	return (error);
1377139584Srwatson}
1378139584Srwatson
137924659Sjhay/*
138024659Sjhay * Initiate connection to peer.
138124659Sjhay * Enter SYN_SENT state, and mark socket as connecting.
138224659Sjhay * Start keep-alive timer, setup prototype header,
138324659Sjhay * Send initial system packet requesting connection.
138424659Sjhay */
138524659Sjhaystatic int
1386157067Srwatsonspx_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
138724659Sjhay{
138824659Sjhay	struct ipxpcb *ipxp;
138924659Sjhay	struct spxpcb *cb;
1390139932Srwatson	int error;
139111819Sjulian
139224659Sjhay	ipxp = sotoipxpcb(so);
139324659Sjhay	cb = ipxtospxpcb(ipxp);
139424659Sjhay
1395139932Srwatson	IPX_LIST_LOCK();
1396139932Srwatson	IPX_LOCK(ipxp);
139724659Sjhay	if (ipxp->ipxp_lport == 0) {
1398139579Srwatson		error = ipx_pcbbind(ipxp, NULL, td);
139924659Sjhay		if (error)
140024659Sjhay			goto spx_connect_end;
140124659Sjhay	}
140283366Sjulian	error = ipx_pcbconnect(ipxp, nam, td);
140324659Sjhay	if (error)
140424659Sjhay		goto spx_connect_end;
140524659Sjhay	soisconnecting(so);
140624659Sjhay	spxstat.spxs_connattempt++;
140724659Sjhay	cb->s_state = TCPS_SYN_SENT;
140824659Sjhay	cb->s_did = 0;
140924659Sjhay	spx_template(cb);
141024659Sjhay	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
141124659Sjhay	cb->s_force = 1 + SPXTV_KEEP;
141211819Sjulian	/*
141324659Sjhay	 * Other party is required to respond to
141424659Sjhay	 * the port I send from, but he is not
141524659Sjhay	 * required to answer from where I am sending to,
141624659Sjhay	 * so allow wildcarding.
141724659Sjhay	 * original port I am sending to is still saved in
141824659Sjhay	 * cb->s_dport.
141911819Sjulian	 */
142024659Sjhay	ipxp->ipxp_fport = 0;
1421139579Srwatson	error = spx_output(cb, NULL);
142224659Sjhayspx_connect_end:
1423139932Srwatson	IPX_UNLOCK(ipxp);
1424139932Srwatson	IPX_LIST_UNLOCK();
142524659Sjhay	return (error);
142624659Sjhay}
142711819Sjulian
142824659Sjhaystatic int
1429157067Srwatsonspx_detach(struct socket *so)
143024659Sjhay{
143124659Sjhay	struct ipxpcb *ipxp;
143224659Sjhay	struct spxpcb *cb;
143311819Sjulian
143424659Sjhay	ipxp = sotoipxpcb(so);
143524659Sjhay	cb = ipxtospxpcb(ipxp);
143611819Sjulian
1437139932Srwatson	IPX_LIST_LOCK();
1438139932Srwatson	IPX_LOCK(ipxp);
143924659Sjhay	if (cb->s_state > TCPS_LISTEN)
144024659Sjhay		spx_disconnect(cb);
144124659Sjhay	else
144224659Sjhay		spx_close(cb);
1443139932Srwatson	IPX_LIST_UNLOCK();
144424659Sjhay	return (0);
144524659Sjhay}
144611819Sjulian
144724659Sjhay/*
144824659Sjhay * We may decide later to implement connection closing
144924659Sjhay * handshaking at the spx level optionally.
145024659Sjhay * here is the hook to do it:
145124659Sjhay */
145224659Sjhaystatic int
1453157067Srwatsonspx_usr_disconnect(struct socket *so)
145424659Sjhay{
145524659Sjhay	struct ipxpcb *ipxp;
145624659Sjhay	struct spxpcb *cb;
145711819Sjulian
145824659Sjhay	ipxp = sotoipxpcb(so);
145924659Sjhay	cb = ipxtospxpcb(ipxp);
146011819Sjulian
1461139932Srwatson	IPX_LIST_LOCK();
1462139932Srwatson	IPX_LOCK(ipxp);
146324659Sjhay	spx_disconnect(cb);
1464139932Srwatson	IPX_LIST_UNLOCK();
146524659Sjhay	return (0);
146624659Sjhay}
146711819Sjulian
146824659Sjhaystatic int
1469157067Srwatsonspx_listen(struct socket *so, int backlog, struct thread *td)
147024659Sjhay{
147124659Sjhay	int error;
147224659Sjhay	struct ipxpcb *ipxp;
147324659Sjhay	struct spxpcb *cb;
147411819Sjulian
147524659Sjhay	error = 0;
147624659Sjhay	ipxp = sotoipxpcb(so);
147724659Sjhay	cb = ipxtospxpcb(ipxp);
147811819Sjulian
1479139932Srwatson	IPX_LIST_LOCK();
1480139932Srwatson	IPX_LOCK(ipxp);
1481142190Srwatson	SOCK_LOCK(so);
1482142190Srwatson	error = solisten_proto_check(so);
1483142190Srwatson	if (error == 0 && ipxp->ipxp_lport == 0)
1484139579Srwatson		error = ipx_pcbbind(ipxp, NULL, td);
1485142190Srwatson	if (error == 0) {
148624659Sjhay		cb->s_state = TCPS_LISTEN;
1487151888Srwatson		solisten_proto(so, backlog);
1488142190Srwatson	}
1489142190Srwatson	SOCK_UNLOCK(so);
1490139932Srwatson	IPX_UNLOCK(ipxp);
1491139932Srwatson	IPX_LIST_UNLOCK();
149224659Sjhay	return (error);
149324659Sjhay}
149411819Sjulian
149524659Sjhay/*
149624659Sjhay * After a receive, possibly send acknowledgment
149724659Sjhay * updating allocation.
149824659Sjhay */
149924659Sjhaystatic int
1500157067Srwatsonspx_rcvd(struct socket *so, int flags)
150124659Sjhay{
150224659Sjhay	struct ipxpcb *ipxp;
150324659Sjhay	struct spxpcb *cb;
150411819Sjulian
150524659Sjhay	ipxp = sotoipxpcb(so);
150624659Sjhay	cb = ipxtospxpcb(ipxp);
150711819Sjulian
1508139932Srwatson	IPX_LOCK(ipxp);
150924659Sjhay	cb->s_flags |= SF_RVD;
1510139579Srwatson	spx_output(cb, NULL);
151124659Sjhay	cb->s_flags &= ~SF_RVD;
1512139932Srwatson	IPX_UNLOCK(ipxp);
151324659Sjhay	return (0);
151424659Sjhay}
151511819Sjulian
151624659Sjhaystatic int
1517157067Srwatsonspx_rcvoob(struct socket *so, struct mbuf *m, int flags)
151824659Sjhay{
151924659Sjhay	struct ipxpcb *ipxp;
152024659Sjhay	struct spxpcb *cb;
152111819Sjulian
152224659Sjhay	ipxp = sotoipxpcb(so);
152324659Sjhay	cb = ipxtospxpcb(ipxp);
152411819Sjulian
1525139591Srwatson	SOCKBUF_LOCK(&so->so_rcv);
152624659Sjhay	if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark ||
1527130480Srwatson	    (so->so_rcv.sb_state & SBS_RCVATMARK)) {
1528139591Srwatson		SOCKBUF_UNLOCK(&so->so_rcv);
152924659Sjhay		m->m_len = 1;
1530139932Srwatson		/* Unlocked read. */
153124659Sjhay		*mtod(m, caddr_t) = cb->s_iobc;
153224659Sjhay		return (0);
153311819Sjulian	}
1534139591Srwatson	SOCKBUF_UNLOCK(&so->so_rcv);
153524659Sjhay	return (EINVAL);
153624659Sjhay}
153724659Sjhay
153824659Sjhaystatic int
1539157067Srwatsonspx_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1540157067Srwatson    struct mbuf *controlp, struct thread *td)
154124659Sjhay{
154224659Sjhay	int error;
154324659Sjhay	struct ipxpcb *ipxp;
154424659Sjhay	struct spxpcb *cb;
154524659Sjhay
154624659Sjhay	error = 0;
154724659Sjhay	ipxp = sotoipxpcb(so);
154824659Sjhay	cb = ipxtospxpcb(ipxp);
154924659Sjhay
1550139932Srwatson	IPX_LOCK(ipxp);
155124659Sjhay	if (flags & PRUS_OOB) {
155224659Sjhay		if (sbspace(&so->so_snd) < -512) {
155324659Sjhay			error = ENOBUFS;
155424659Sjhay			goto spx_send_end;
155524659Sjhay		}
155624659Sjhay		cb->s_oobflags |= SF_SOOB;
155724659Sjhay	}
155825652Sjhay	if (controlp != NULL) {
155924659Sjhay		u_short *p = mtod(controlp, u_short *);
156024659Sjhay		spx_newchecks[2]++;
156125652Sjhay		if ((p[0] == 5) && (p[1] == 1)) { /* XXXX, for testing */
156224659Sjhay			cb->s_shdr.spx_dt = *(u_char *)(&p[2]);
156324659Sjhay			spx_newchecks[3]++;
156424659Sjhay		}
156524659Sjhay		m_freem(controlp);
156624659Sjhay	}
156724659Sjhay	controlp = NULL;
156824659Sjhay	error = spx_output(cb, m);
156924659Sjhay	m = NULL;
157024659Sjhayspx_send_end:
1571139932Srwatson	IPX_UNLOCK(ipxp);
157211819Sjulian	if (controlp != NULL)
157311819Sjulian		m_freem(controlp);
157411819Sjulian	if (m != NULL)
157511819Sjulian		m_freem(m);
157611819Sjulian	return (error);
157711819Sjulian}
157811819Sjulian
157924659Sjhaystatic int
1580157067Srwatsonspx_shutdown(struct socket *so)
158124659Sjhay{
158224659Sjhay	struct ipxpcb *ipxp;
158324659Sjhay	struct spxpcb *cb;
158424659Sjhay
158524659Sjhay	ipxp = sotoipxpcb(so);
158624659Sjhay	cb = ipxtospxpcb(ipxp);
158724659Sjhay
158824659Sjhay	socantsendmore(so);
1589139932Srwatson	IPX_LIST_LOCK();
1590139932Srwatson	IPX_LOCK(ipxp);
1591139931Srwatson	spx_usrclosed(cb);
1592139932Srwatson	IPX_LIST_UNLOCK();
1593139931Srwatson	return (0);
159424659Sjhay}
159524659Sjhay
159624659Sjhaystatic int
1597157067Srwatsonspx_sp_attach(struct socket *so, int proto, struct thread *td)
159811819Sjulian{
159924659Sjhay	int error;
160024659Sjhay	struct ipxpcb *ipxp;
160111819Sjulian
160283366Sjulian	error = spx_attach(so, proto, td);
160324659Sjhay	if (error == 0) {
160424659Sjhay		ipxp = sotoipxpcb(so);
160511819Sjulian		((struct spxpcb *)ipxp->ipxp_pcb)->s_flags |=
160611819Sjulian					(SF_HI | SF_HO | SF_PI);
160711819Sjulian	}
160811819Sjulian	return (error);
160911819Sjulian}
161011819Sjulian
161111819Sjulian/*
161211819Sjulian * Create template to be used to send spx packets on a connection.
161311819Sjulian * Called after host entry created, fills
161411819Sjulian * in a skeletal spx header (choosing connection id),
161511819Sjulian * minimizing the amount of work necessary when the connection is used.
161611819Sjulian */
161725652Sjhaystatic void
1618157067Srwatsonspx_template(struct spxpcb *cb)
161911819Sjulian{
1620157067Srwatson	struct ipxpcb *ipxp = cb->s_ipxpcb;
1621157067Srwatson	struct ipx *ipx = cb->s_ipx;
1622157067Srwatson	struct sockbuf *sb = &(ipxp->ipxp_socket->so_snd);
162311819Sjulian
1624139932Srwatson	IPX_LOCK_ASSERT(ipxp);
1625139932Srwatson
162611819Sjulian	ipx->ipx_pt = IPXPROTO_SPX;
162711819Sjulian	ipx->ipx_sna = ipxp->ipxp_laddr;
162811819Sjulian	ipx->ipx_dna = ipxp->ipxp_faddr;
162911819Sjulian	cb->s_sid = htons(spx_iss);
163011819Sjulian	spx_iss += SPX_ISSINCR/2;
163111819Sjulian	cb->s_alo = 1;
163211819Sjulian	cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu;
163311819Sjulian	cb->s_ssthresh = cb->s_cwnd; /* Try to expand fast to full complement
163411819Sjulian					of large packets */
163511819Sjulian	cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spx));
163611819Sjulian	cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd);
163711819Sjulian		/* But allow for lots of little packets as well */
163811819Sjulian}
163911819Sjulian
164011819Sjulian/*
164111819Sjulian * Close a SPIP control block:
164211819Sjulian *	discard spx control block itself
164311819Sjulian *	discard ipx protocol control block
164411819Sjulian *	wake up any sleepers
1645139931Srwatson * cb will always be invalid after this call.
164611819Sjulian */
1647139931Srwatsonvoid
1648157067Srwatsonspx_close(struct spxpcb *cb)
164911819Sjulian{
1650157067Srwatson	struct spx_q *s;
165111819Sjulian	struct ipxpcb *ipxp = cb->s_ipxpcb;
165211819Sjulian	struct socket *so = ipxp->ipxp_socket;
1653157067Srwatson	struct mbuf *m;
165411819Sjulian
1655139932Srwatson	IPX_LIST_LOCK_ASSERT();
1656139932Srwatson	IPX_LOCK_ASSERT(ipxp);
1657139932Srwatson
165811819Sjulian	s = cb->s_q.si_next;
165911819Sjulian	while (s != &(cb->s_q)) {
166011819Sjulian		s = s->si_next;
166111819Sjulian		m = dtom(s->si_prev);
166211819Sjulian		remque(s->si_prev);
166311819Sjulian		m_freem(m);
166411819Sjulian	}
166525652Sjhay	m_free(dtom(cb->s_ipx));
166628270Swollman	FREE(cb, M_PCB);
1667139580Srwatson	ipxp->ipxp_pcb = NULL;
166811819Sjulian	soisdisconnected(so);
166911819Sjulian	ipx_pcbdetach(ipxp);
167011819Sjulian	spxstat.spxs_closed++;
167111819Sjulian}
167225652Sjhay
167311819Sjulian/*
167411819Sjulian *	Someday we may do level 3 handshaking
167511819Sjulian *	to close a connection or send a xerox style error.
167611819Sjulian *	For now, just close.
1677139931Srwatson * cb will always be invalid after this call.
167811819Sjulian */
1679139931Srwatsonstatic void
1680157067Srwatsonspx_usrclosed(struct spxpcb *cb)
168111819Sjulian{
1682139931Srwatson
1683139932Srwatson	IPX_LIST_LOCK_ASSERT();
1684139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1685139932Srwatson
1686139931Srwatson	spx_close(cb);
168711819Sjulian}
168825652Sjhay
1689139931Srwatson/*
1690139931Srwatson * cb will always be invalid after this call.
1691139931Srwatson */
1692139931Srwatsonstatic void
1693157067Srwatsonspx_disconnect(struct spxpcb *cb)
169411819Sjulian{
1695139931Srwatson
1696139932Srwatson	IPX_LIST_LOCK_ASSERT();
1697139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1698139932Srwatson
1699139931Srwatson	spx_close(cb);
170011819Sjulian}
170125652Sjhay
170211819Sjulian/*
170311819Sjulian * Drop connection, reporting
170411819Sjulian * the specified error.
1705139931Srwatson * cb will always be invalid after this call.
170611819Sjulian */
1707139931Srwatsonstatic void
1708157067Srwatsonspx_drop(struct spxpcb *cb, int errno)
170911819Sjulian{
171011819Sjulian	struct socket *so = cb->s_ipxpcb->ipxp_socket;
171111819Sjulian
1712139932Srwatson	IPX_LIST_LOCK_ASSERT();
1713139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1714139932Srwatson
171511819Sjulian	/*
171611819Sjulian	 * someday, in the xerox world
171711819Sjulian	 * we will generate error protocol packets
171811819Sjulian	 * announcing that the socket has gone away.
171911819Sjulian	 */
172011819Sjulian	if (TCPS_HAVERCVDSYN(cb->s_state)) {
172111819Sjulian		spxstat.spxs_drops++;
172211819Sjulian		cb->s_state = TCPS_CLOSED;
172325652Sjhay		/*tcp_output(cb);*/
172411819Sjulian	} else
172511819Sjulian		spxstat.spxs_conndrops++;
172611819Sjulian	so->so_error = errno;
1727139931Srwatson	spx_close(cb);
172811819Sjulian}
172911819Sjulian
173011819Sjulian/*
173111819Sjulian * Fast timeout routine for processing delayed acks
173211819Sjulian */
173311819Sjulianvoid
1734157067Srwatsonspx_fasttimo(void)
173511819Sjulian{
1736139932Srwatson	struct ipxpcb *ipxp;
1737139932Srwatson	struct spxpcb *cb;
173811819Sjulian
1739139932Srwatson	IPX_LIST_LOCK();
1740139444Srwatson	LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) {
1741139932Srwatson		IPX_LOCK(ipxp);
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		}
1749139932Srwatson		IPX_UNLOCK(ipxp);
1750139444Srwatson	}
1751139932Srwatson	IPX_LIST_UNLOCK();
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
1760157067Srwatsonspx_slowtimo(void)
176111819Sjulian{
1762139932Srwatson	struct ipxpcb *ip, *ip_temp;
1763139932Srwatson	struct spxpcb *cb;
1764139932Srwatson	int i;
176511819Sjulian
176611819Sjulian	/*
1767139444Srwatson	 * Search through tcb's and update active timers.  Note that timers
1768139444Srwatson	 * may free the ipxpcb, so be sure to handle that case.
1769139932Srwatson	 *
1770139932Srwatson	 * spx_timers() may remove an ipxpcb entry, so we have to be ready to
1771139932Srwatson	 * continue despite that.  The logic here is a bit obfuscated.
177211819Sjulian	 */
1773139932Srwatson	IPX_LIST_LOCK();
1774139444Srwatson	LIST_FOREACH_SAFE(ip, &ipxpcb_list, ipxp_list, ip_temp) {
177511819Sjulian		cb = ipxtospxpcb(ip);
177625652Sjhay		if (cb == NULL)
1777139444Srwatson			continue;
1778139932Srwatson		IPX_LOCK(cb->s_ipxpcb);
177911819Sjulian		for (i = 0; i < SPXT_NTIMERS; i++) {
178011819Sjulian			if (cb->s_timer[i] && --cb->s_timer[i] == 0) {
1781139444Srwatson				/*
1782139444Srwatson				 * spx_timers() returns (NULL) if it free'd
1783139444Srwatson				 * the pcb.
1784139444Srwatson				 */
1785139581Srwatson				cb = spx_timers(cb, i);
1786139581Srwatson				if (cb == NULL)
1787139581Srwatson					break;
178811819Sjulian			}
178911819Sjulian		}
1790139581Srwatson		if (cb != NULL) {
1791139581Srwatson			cb->s_idle++;
1792139581Srwatson			if (cb->s_rtt)
1793139581Srwatson				cb->s_rtt++;
1794139932Srwatson			IPX_UNLOCK(cb->s_ipxpcb);
1795139581Srwatson		}
179611819Sjulian	}
179711819Sjulian	spx_iss += SPX_ISSINCR/PR_SLOWHZ;		/* increment iss */
1798139932Srwatson	IPX_LIST_UNLOCK();
179911819Sjulian}
180025652Sjhay
180111819Sjulian/*
180211819Sjulian * SPX timer processing.
180311819Sjulian */
180425652Sjhaystatic struct spxpcb *
1805157067Srwatsonspx_timers(struct spxpcb *cb, int timer)
180611819Sjulian{
180711819Sjulian	long rexmt;
180811819Sjulian	int win;
180911819Sjulian
1810139932Srwatson	IPX_LIST_LOCK_ASSERT();
1811139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1812139932Srwatson
181311819Sjulian	cb->s_force = 1 + timer;
181411819Sjulian	switch (timer) {
181511819Sjulian
181611819Sjulian	/*
181711819Sjulian	 * 2 MSL timeout in shutdown went off.  TCP deletes connection
181811819Sjulian	 * control block.
181911819Sjulian	 */
182011819Sjulian	case SPXT_2MSL:
182111819Sjulian		printf("spx: SPXT_2MSL went off for no reason\n");
182211819Sjulian		cb->s_timer[timer] = 0;
182311819Sjulian		break;
182411819Sjulian
182511819Sjulian	/*
182611819Sjulian	 * Retransmission timer went off.  Message has not
182711819Sjulian	 * been acked within retransmit interval.  Back off
182811819Sjulian	 * to a longer retransmit interval and retransmit one packet.
182911819Sjulian	 */
183011819Sjulian	case SPXT_REXMT:
183111819Sjulian		if (++cb->s_rxtshift > SPX_MAXRXTSHIFT) {
183211819Sjulian			cb->s_rxtshift = SPX_MAXRXTSHIFT;
183311819Sjulian			spxstat.spxs_timeoutdrop++;
1834139931Srwatson			spx_drop(cb, ETIMEDOUT);
1835139931Srwatson			cb = NULL;
183611819Sjulian			break;
183711819Sjulian		}
183811819Sjulian		spxstat.spxs_rexmttimeo++;
183911819Sjulian		rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
184011819Sjulian		rexmt *= spx_backoff[cb->s_rxtshift];
184111819Sjulian		SPXT_RANGESET(cb->s_rxtcur, rexmt, SPXTV_MIN, SPXTV_REXMTMAX);
184211819Sjulian		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
184311819Sjulian		/*
184411819Sjulian		 * If we have backed off fairly far, our srtt
184511819Sjulian		 * estimate is probably bogus.  Clobber it
184611819Sjulian		 * so we'll take the next rtt measurement as our srtt;
184711819Sjulian		 * move the current srtt into rttvar to keep the current
184811819Sjulian		 * retransmit times until then.
184911819Sjulian		 */
185011819Sjulian		if (cb->s_rxtshift > SPX_MAXRXTSHIFT / 4 ) {
185111819Sjulian			cb->s_rttvar += (cb->s_srtt >> 2);
185211819Sjulian			cb->s_srtt = 0;
185311819Sjulian		}
185411819Sjulian		cb->s_snxt = cb->s_rack;
185511819Sjulian		/*
185611819Sjulian		 * If timing a packet, stop the timer.
185711819Sjulian		 */
185811819Sjulian		cb->s_rtt = 0;
185911819Sjulian		/*
186011819Sjulian		 * See very long discussion in tcp_timer.c about congestion
186111819Sjulian		 * window and sstrhesh
186211819Sjulian		 */
186311819Sjulian		win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2;
186411819Sjulian		if (win < 2)
186511819Sjulian			win = 2;
186611819Sjulian		cb->s_cwnd = CUNIT;
186711819Sjulian		cb->s_ssthresh = win * CUNIT;
1868139579Srwatson		spx_output(cb, NULL);
186911819Sjulian		break;
187011819Sjulian
187111819Sjulian	/*
187211819Sjulian	 * Persistance timer into zero window.
187311819Sjulian	 * Force a probe to be sent.
187411819Sjulian	 */
187511819Sjulian	case SPXT_PERSIST:
187611819Sjulian		spxstat.spxs_persisttimeo++;
187711819Sjulian		spx_setpersist(cb);
1878139579Srwatson		spx_output(cb, NULL);
187911819Sjulian		break;
188011819Sjulian
188111819Sjulian	/*
188211819Sjulian	 * Keep-alive timer went off; send something
188311819Sjulian	 * or drop connection if idle for too long.
188411819Sjulian	 */
188511819Sjulian	case SPXT_KEEP:
188611819Sjulian		spxstat.spxs_keeptimeo++;
188711819Sjulian		if (cb->s_state < TCPS_ESTABLISHED)
188811819Sjulian			goto dropit;
188911819Sjulian		if (cb->s_ipxpcb->ipxp_socket->so_options & SO_KEEPALIVE) {
189011819Sjulian		    	if (cb->s_idle >= SPXTV_MAXIDLE)
189111819Sjulian				goto dropit;
189211819Sjulian			spxstat.spxs_keepprobe++;
1893139579Srwatson			spx_output(cb, NULL);
189497658Stanimura		} else
189511819Sjulian			cb->s_idle = 0;
189611819Sjulian		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
189711819Sjulian		break;
189811819Sjulian	dropit:
189911819Sjulian		spxstat.spxs_keepdrops++;
1900139931Srwatson		spx_drop(cb, ETIMEDOUT);
1901139931Srwatson		cb = NULL;
190211819Sjulian		break;
190311819Sjulian	}
190411819Sjulian	return (cb);
190511819Sjulian}
1906