spx_reass.c revision 179410
1139823Simp/*-
2157067Srwatson * Copyright (c) 1984, 1985, 1986, 1987, 1993
3157067Srwatson *	The Regents of the University of California.
4157067Srwatson * Copyright (c) 2004-2006 Robert N. M. Watson
5157067Srwatson * 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.
15165899Srwatson * 4. Neither the name of the University nor the names of its contributors
16165899Srwatson *    may be used to endorse or promote products derived from this software
17165899Srwatson *    without specific prior written permission.
18165899Srwatson *
19165899Srwatson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20165899Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21165899Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22165899Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23165899Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24165899Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25165899Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26165899Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27165899Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28165899Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29165899Srwatson * SUCH DAMAGE.
30165899Srwatson *
31165899Srwatson * Copyright (c) 1995, Mike Mitchell
32165899Srwatson * All rights reserved.
33165899Srwatson *
34165899Srwatson * Redistribution and use in source and binary forms, with or without
35165899Srwatson * modification, are permitted provided that the following conditions
36165899Srwatson * are met:
37165899Srwatson * 1. Redistributions of source code must retain the above copyright
38165899Srwatson *    notice, this list of conditions and the following disclaimer.
39165899Srwatson * 2. Redistributions in binary form must reproduce the above copyright
40165899Srwatson *    notice, this list of conditions and the following disclaimer in the
41165899Srwatson *    documentation and/or other materials provided with the distribution.
4211819Sjulian * 3. All advertising materials mentioning features or use of this software
4311819Sjulian *    must display the following acknowledgement:
4411819Sjulian *	This product includes software developed by the University of
4511819Sjulian *	California, Berkeley and its contributors.
4611819Sjulian * 4. Neither the name of the University nor the names of its contributors
4711819Sjulian *    may be used to endorse or promote products derived from this software
4811819Sjulian *    without specific prior written permission.
4911819Sjulian *
5011819Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5111819Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5211819Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5311819Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5411819Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5511819Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5611819Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5711819Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5811819Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5911819Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6011819Sjulian * SUCH DAMAGE.
6111819Sjulian *
6212057Sjulian *	@(#)spx_usrreq.h
6311819Sjulian */
6411819Sjulian
65116189Sobrien#include <sys/cdefs.h>
66116189Sobrien__FBSDID("$FreeBSD: head/sys/netipx/spx_usrreq.c 179410 2008-05-29 07:18:43Z rwatson $");
67116189Sobrien
6811819Sjulian#include <sys/param.h>
6976166Smarkm#include <sys/lock.h>
7029024Sbde#include <sys/malloc.h>
7111819Sjulian#include <sys/mbuf.h>
7276166Smarkm#include <sys/mutex.h>
7325345Sjhay#include <sys/proc.h>
7411819Sjulian#include <sys/protosw.h>
7595759Stanimura#include <sys/signalvar.h>
7611819Sjulian#include <sys/socket.h>
7711819Sjulian#include <sys/socketvar.h>
7895759Stanimura#include <sys/sx.h>
7995759Stanimura#include <sys/systm.h>
8011819Sjulian
8111819Sjulian#include <net/route.h>
8211819Sjulian#include <netinet/tcp_fsm.h>
8311819Sjulian
8411819Sjulian#include <netipx/ipx.h>
8511819Sjulian#include <netipx/ipx_pcb.h>
8611819Sjulian#include <netipx/ipx_var.h>
8711819Sjulian#include <netipx/spx.h>
8895759Stanimura#include <netipx/spx_debug.h>
8911819Sjulian#include <netipx/spx_timer.h>
9011819Sjulian#include <netipx/spx_var.h>
9111819Sjulian
9211819Sjulian/*
9311819Sjulian * SPX protocol implementation.
9411819Sjulian */
95157069Srwatsonstatic struct	mtx spx_mtx;			/* Protects only spx_iss. */
9633181Seivindstatic u_short 	spx_iss;
9733181Seivindstatic u_short	spx_newchecks[50];
9833181Seivindstatic int	spx_hardnosed;
9933181Seivindstatic int	spx_use_delack = 0;
10033181Seivindstatic int	traceallspxs = 0;
10133181Seivindstatic struct	spx_istat spx_istat;
102157068Srwatsonstatic int	spxrexmtthresh = 3;
10311819Sjulian
104157069Srwatson#define	SPX_LOCK_INIT()	mtx_init(&spx_mtx, "spx_mtx", NULL, MTX_DEF)
105157069Srwatson#define	SPX_LOCK()	mtx_lock(&spx_mtx)
106157069Srwatson#define	SPX_UNLOCK()	mtx_unlock(&spx_mtx)
107157069Srwatson
10825652Sjhay/* Following was struct spxstat spxstat; */
109139584Srwatson#ifndef spxstat
11025652Sjhay#define spxstat spx_istat.newstats
111139584Srwatson#endif
11211819Sjulian
113132045Srwatsonstatic const int spx_backoff[SPX_MAXRXTSHIFT+1] =
11425652Sjhay    { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
11511819Sjulian
116139931Srwatsonstatic	void spx_close(struct spxpcb *cb);
117139931Srwatsonstatic	void spx_disconnect(struct spxpcb *cb);
118139931Srwatsonstatic	void spx_drop(struct spxpcb *cb, int errno);
11925652Sjhaystatic	int spx_output(struct spxpcb *cb, struct mbuf *m0);
12025652Sjhaystatic	int spx_reass(struct spxpcb *cb, struct spx *si);
12125652Sjhaystatic	void spx_setpersist(struct spxpcb *cb);
12225652Sjhaystatic	void spx_template(struct spxpcb *cb);
123157128Srwatsonstatic	void spx_timers(struct spxpcb *cb, int timer);
124139931Srwatsonstatic	void spx_usrclosed(struct spxpcb *cb);
12525652Sjhay
126157366Srwatsonstatic	void spx_usr_abort(struct socket *so);
12728270Swollmanstatic	int spx_accept(struct socket *so, struct sockaddr **nam);
12883366Sjulianstatic	int spx_attach(struct socket *so, int proto, struct thread *td);
12983366Sjulianstatic	int spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td);
130160549Srwatsonstatic	void spx_usr_close(struct socket *so);
13128270Swollmanstatic	int spx_connect(struct socket *so, struct sockaddr *nam,
13283366Sjulian			struct thread *td);
133157370Srwatsonstatic	void spx_detach(struct socket *so);
134157128Srwatsonstatic	void spx_pcbdetach(struct ipxpcb *ipxp);
13524659Sjhaystatic	int spx_usr_disconnect(struct socket *so);
136151888Srwatsonstatic	int spx_listen(struct socket *so, int backlog, struct thread *td);
13724659Sjhaystatic	int spx_rcvd(struct socket *so, int flags);
13824659Sjhaystatic	int spx_rcvoob(struct socket *so, struct mbuf *m, int flags);
13924659Sjhaystatic	int spx_send(struct socket *so, int flags, struct mbuf *m,
140139584Srwatson		     struct sockaddr *addr, struct mbuf *control,
14183366Sjulian		     struct thread *td);
14224659Sjhaystatic	int spx_shutdown(struct socket *so);
14383366Sjulianstatic	int spx_sp_attach(struct socket *so, int proto, struct thread *td);
14424659Sjhay
14524659Sjhaystruct	pr_usrreqs spx_usrreqs = {
146137386Sphk	.pru_abort =		spx_usr_abort,
147137386Sphk	.pru_accept =		spx_accept,
148137386Sphk	.pru_attach =		spx_attach,
149137386Sphk	.pru_bind =		spx_bind,
150137386Sphk	.pru_connect =		spx_connect,
151137386Sphk	.pru_control =		ipx_control,
152137386Sphk	.pru_detach =		spx_detach,
153137386Sphk	.pru_disconnect =	spx_usr_disconnect,
154137386Sphk	.pru_listen =		spx_listen,
155137386Sphk	.pru_peeraddr =		ipx_peeraddr,
156137386Sphk	.pru_rcvd =		spx_rcvd,
157137386Sphk	.pru_rcvoob =		spx_rcvoob,
158137386Sphk	.pru_send =		spx_send,
159137386Sphk	.pru_shutdown =		spx_shutdown,
160137386Sphk	.pru_sockaddr =		ipx_sockaddr,
161160549Srwatson	.pru_close =		spx_usr_close,
16224659Sjhay};
16324659Sjhay
16424659Sjhaystruct	pr_usrreqs spx_usrreq_sps = {
165137386Sphk	.pru_abort =		spx_usr_abort,
166137386Sphk	.pru_accept =		spx_accept,
167137386Sphk	.pru_attach =		spx_sp_attach,
168137386Sphk	.pru_bind =		spx_bind,
169137386Sphk	.pru_connect =		spx_connect,
170137386Sphk	.pru_control =		ipx_control,
171137386Sphk	.pru_detach =		spx_detach,
172137386Sphk	.pru_disconnect =	spx_usr_disconnect,
173137386Sphk	.pru_listen =		spx_listen,
174137386Sphk	.pru_peeraddr =		ipx_peeraddr,
175137386Sphk	.pru_rcvd =		spx_rcvd,
176137386Sphk	.pru_rcvoob =		spx_rcvoob,
177137386Sphk	.pru_send =		spx_send,
178137386Sphk	.pru_shutdown =		spx_shutdown,
179137386Sphk	.pru_sockaddr =		ipx_sockaddr,
180160549Srwatson	.pru_close =		spx_usr_close,
18124659Sjhay};
18224659Sjhay
18311819Sjulianvoid
184157067Srwatsonspx_init(void)
18511819Sjulian{
18611819Sjulian
187157069Srwatson	SPX_LOCK_INIT();
18811819Sjulian	spx_iss = 1; /* WRONG !! should fish it out of TODR */
18911819Sjulian}
19011819Sjulian
19111819Sjulianvoid
192157067Srwatsonspx_input(struct mbuf *m, struct ipxpcb *ipxp)
19311819Sjulian{
194157067Srwatson	struct spxpcb *cb;
195157067Srwatson	struct spx *si = mtod(m, struct spx *);
196157067Srwatson	struct socket *so;
197157051Srwatson	struct spx spx_savesi;
19811819Sjulian	int dropsocket = 0;
19911819Sjulian	short ostate = 0;
20011819Sjulian
20111819Sjulian	spxstat.spxs_rcvtotal++;
202157094Srwatson	KASSERT(ipxp != NULL, ("spx_input: ipxpcb == NULL"));
20311819Sjulian
204139932Srwatson	/*
205139932Srwatson	 * spx_input() assumes that the caller will hold both the pcb list
206139932Srwatson	 * lock and also the ipxp lock.  spx_input() will release both before
207139932Srwatson	 * returning, and may in fact trade in the ipxp lock for another pcb
208139932Srwatson	 * lock following sonewconn().
209139932Srwatson	 */
210139932Srwatson	IPX_LIST_LOCK_ASSERT();
211139932Srwatson	IPX_LOCK_ASSERT(ipxp);
212139932Srwatson
21311819Sjulian	cb = ipxtospxpcb(ipxp);
214157128Srwatson	KASSERT(cb != NULL, ("spx_input: cb == NULL"));
21511819Sjulian
216157128Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED)
217157128Srwatson		goto drop;
218157128Srwatson
21911819Sjulian	if (m->m_len < sizeof(*si)) {
22025652Sjhay		if ((m = m_pullup(m, sizeof(*si))) == NULL) {
221139932Srwatson			IPX_UNLOCK(ipxp);
222139932Srwatson			IPX_LIST_UNLOCK();
22311819Sjulian			spxstat.spxs_rcvshort++;
22411819Sjulian			return;
22511819Sjulian		}
22611819Sjulian		si = mtod(m, struct spx *);
22711819Sjulian	}
22811819Sjulian	si->si_seq = ntohs(si->si_seq);
22911819Sjulian	si->si_ack = ntohs(si->si_ack);
23011819Sjulian	si->si_alo = ntohs(si->si_alo);
23111819Sjulian
23211819Sjulian	so = ipxp->ipxp_socket;
233157094Srwatson	KASSERT(so != NULL, ("spx_input: so == NULL"));
23411819Sjulian
23511819Sjulian	if (so->so_options & SO_DEBUG || traceallspxs) {
23611819Sjulian		ostate = cb->s_state;
23711819Sjulian		spx_savesi = *si;
23811819Sjulian	}
23911819Sjulian	if (so->so_options & SO_ACCEPTCONN) {
24011819Sjulian		struct spxpcb *ocb = cb;
24111819Sjulian
24211819Sjulian		so = sonewconn(so, 0);
243157094Srwatson		if (so == NULL)
24411819Sjulian			goto drop;
245157094Srwatson
24611819Sjulian		/*
24711819Sjulian		 * This is ugly, but ....
24811819Sjulian		 *
249157094Srwatson		 * Mark socket as temporary until we're committed to keeping
250157094Srwatson		 * it.  The code at ``drop'' and ``dropwithreset'' check the
251157094Srwatson		 * flag dropsocket to see if the temporary socket created
252157094Srwatson		 * here should be discarded.  We mark the socket as
253157094Srwatson		 * discardable until we're committed to it below in
254157094Srwatson		 * TCPS_LISTEN.
255157128Srwatson		 *
256157128Srwatson		 * XXXRW: In the new world order of real kernel parallelism,
257157128Srwatson		 * temporarily allocating the socket when we're "not sure"
258157128Srwatson		 * seems like a bad idea, as we might race to remove it if
259157128Srwatson		 * the listen socket is closed...?
260157128Srwatson		 *
261157128Srwatson		 * We drop the lock of the listen socket ipxp, and acquire
262157128Srwatson		 * the lock of the new socket ippx.
26311819Sjulian		 */
26411819Sjulian		dropsocket++;
265139932Srwatson		IPX_UNLOCK(ipxp);
26611819Sjulian		ipxp = (struct ipxpcb *)so->so_pcb;
267139932Srwatson		IPX_LOCK(ipxp);
26811819Sjulian		ipxp->ipxp_laddr = si->si_dna;
26911819Sjulian		cb = ipxtospxpcb(ipxp);
27011819Sjulian		cb->s_mtu = ocb->s_mtu;		/* preserve sockopts */
27111819Sjulian		cb->s_flags = ocb->s_flags;	/* preserve sockopts */
27211819Sjulian		cb->s_flags2 = ocb->s_flags2;	/* preserve sockopts */
27311819Sjulian		cb->s_state = TCPS_LISTEN;
27497658Stanimura	}
275157094Srwatson	IPX_LOCK_ASSERT(ipxp);
27611819Sjulian
27711819Sjulian	/*
278157094Srwatson	 * Packet received on connection.  Reset idle time and keep-alive
279157094Srwatson	 * timer.
28011819Sjulian	 */
28111819Sjulian	cb->s_idle = 0;
28211819Sjulian	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
28311819Sjulian
28411819Sjulian	switch (cb->s_state) {
28511819Sjulian	case TCPS_LISTEN:{
28628270Swollman		struct sockaddr_ipx *sipx, ssipx;
28711819Sjulian		struct ipx_addr laddr;
28811819Sjulian
28911819Sjulian		/*
290157094Srwatson		 * If somebody here was carying on a conversation and went
291157094Srwatson		 * away, and his pen pal thinks he can still talk, we get the
292157094Srwatson		 * misdirected packet.
29311819Sjulian		 */
29411819Sjulian		if (spx_hardnosed && (si->si_did != 0 || si->si_seq != 0)) {
29511819Sjulian			spx_istat.gonawy++;
29611819Sjulian			goto dropwithreset;
29711819Sjulian		}
29828270Swollman		sipx = &ssipx;
29928270Swollman		bzero(sipx, sizeof *sipx);
30011819Sjulian		sipx->sipx_len = sizeof(*sipx);
30111819Sjulian		sipx->sipx_family = AF_IPX;
30211819Sjulian		sipx->sipx_addr = si->si_sna;
30311819Sjulian		laddr = ipxp->ipxp_laddr;
30411819Sjulian		if (ipx_nullhost(laddr))
30511819Sjulian			ipxp->ipxp_laddr = si->si_dna;
30690361Sjulian		if (ipx_pcbconnect(ipxp, (struct sockaddr *)sipx, &thread0)) {
30711819Sjulian			ipxp->ipxp_laddr = laddr;
30811819Sjulian			spx_istat.noconn++;
30911819Sjulian			goto drop;
31011819Sjulian		}
31111819Sjulian		spx_template(cb);
31211819Sjulian		dropsocket = 0;		/* committed to socket */
31311819Sjulian		cb->s_did = si->si_sid;
31411819Sjulian		cb->s_rack = si->si_ack;
31511819Sjulian		cb->s_ralo = si->si_alo;
31611819Sjulian#define THREEWAYSHAKE
31711819Sjulian#ifdef THREEWAYSHAKE
31811819Sjulian		cb->s_state = TCPS_SYN_RECEIVED;
31911819Sjulian		cb->s_force = 1 + SPXT_KEEP;
32011819Sjulian		spxstat.spxs_accepts++;
32111819Sjulian		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
32211819Sjulian		}
32311819Sjulian		break;
324157094Srwatson
32511819Sjulian	 case TCPS_SYN_RECEIVED: {
326157094Srwatson		/*
327157094Srwatson		 * This state means that we have heard a response to our
328157094Srwatson		 * acceptance of their connection.  It is probably logically
329157094Srwatson		 * unnecessary in this implementation.
330157094Srwatson		 */
33125652Sjhay		if (si->si_did != cb->s_sid) {
33211819Sjulian			spx_istat.wrncon++;
33311819Sjulian			goto drop;
33411819Sjulian		}
33511819Sjulian#endif
33611819Sjulian		ipxp->ipxp_fport =  si->si_sport;
33711819Sjulian		cb->s_timer[SPXT_REXMT] = 0;
33811819Sjulian		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
33911819Sjulian		soisconnected(so);
34011819Sjulian		cb->s_state = TCPS_ESTABLISHED;
34111819Sjulian		spxstat.spxs_accepts++;
34211819Sjulian		}
34311819Sjulian		break;
34411819Sjulian
34511819Sjulian	case TCPS_SYN_SENT:
346157094Srwatson		/*
347157094Srwatson		 * This state means that we have gotten a response to our
348157094Srwatson		 * attempt to establish a connection.  We fill in the data
349157094Srwatson		 * from the other side, telling us which port to respond to,
350157094Srwatson		 * instead of the well-known one we might have sent to in the
351157094Srwatson		 * first place.  We also require that this is a response to
352157094Srwatson		 * our connection id.
353157094Srwatson		 */
35425652Sjhay		if (si->si_did != cb->s_sid) {
35511819Sjulian			spx_istat.notme++;
35611819Sjulian			goto drop;
35711819Sjulian		}
35811819Sjulian		spxstat.spxs_connects++;
35911819Sjulian		cb->s_did = si->si_sid;
36011819Sjulian		cb->s_rack = si->si_ack;
36111819Sjulian		cb->s_ralo = si->si_alo;
36211819Sjulian		cb->s_dport = ipxp->ipxp_fport =  si->si_sport;
36311819Sjulian		cb->s_timer[SPXT_REXMT] = 0;
36411819Sjulian		cb->s_flags |= SF_ACKNOW;
36511819Sjulian		soisconnected(so);
36611819Sjulian		cb->s_state = TCPS_ESTABLISHED;
367179408Srwatson
368157094Srwatson		/*
369157094Srwatson		 * Use roundtrip time of connection request for initial rtt.
370157094Srwatson		 */
37111819Sjulian		if (cb->s_rtt) {
37211819Sjulian			cb->s_srtt = cb->s_rtt << 3;
37311819Sjulian			cb->s_rttvar = cb->s_rtt << 1;
37411819Sjulian			SPXT_RANGESET(cb->s_rxtcur,
37511819Sjulian			    ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
37611819Sjulian			    SPXTV_MIN, SPXTV_REXMTMAX);
37711819Sjulian			    cb->s_rtt = 0;
37811819Sjulian		}
37911819Sjulian	}
380157094Srwatson
38197658Stanimura	if (so->so_options & SO_DEBUG || traceallspxs)
38211819Sjulian		spx_trace(SA_INPUT, (u_char)ostate, cb, &spx_savesi, 0);
38311819Sjulian
38425652Sjhay	m->m_len -= sizeof(struct ipx);
38525652Sjhay	m->m_pkthdr.len -= sizeof(struct ipx);
38625652Sjhay	m->m_data += sizeof(struct ipx);
38711819Sjulian
388157094Srwatson	if (spx_reass(cb, si))
38925652Sjhay		m_freem(m);
39011819Sjulian	if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT)))
391139579Srwatson		spx_output(cb, NULL);
39211819Sjulian	cb->s_flags &= ~(SF_WIN|SF_RXT);
393139932Srwatson	IPX_UNLOCK(ipxp);
394139932Srwatson	IPX_LIST_UNLOCK();
39511819Sjulian	return;
39611819Sjulian
39711819Sjuliandropwithreset:
398157094Srwatson	IPX_LOCK_ASSERT(ipxp);
399157164Srwatson	if (cb == NULL || (cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG ||
400157128Srwatson	    traceallspxs))
401157128Srwatson		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
402139932Srwatson	IPX_UNLOCK(ipxp);
403130822Srwatson	if (dropsocket) {
404130822Srwatson		struct socket *head;
405130822Srwatson		ACCEPT_LOCK();
406130822Srwatson		KASSERT((so->so_qstate & SQ_INCOMP) != 0,
407130822Srwatson		    ("spx_input: nascent socket not SQ_INCOMP on soabort()"));
408130822Srwatson		head = so->so_head;
409130822Srwatson		TAILQ_REMOVE(&head->so_incomp, so, so_list);
410130822Srwatson		head->so_incqlen--;
411130822Srwatson		so->so_qstate &= ~SQ_INCOMP;
412130822Srwatson		so->so_head = NULL;
413130822Srwatson		ACCEPT_UNLOCK();
41425652Sjhay		soabort(so);
415130822Srwatson	}
416139932Srwatson	IPX_LIST_UNLOCK();
417179332Srwatson	m_freem(m);
41811819Sjulian	return;
41911819Sjulian
42011819Sjuliandrop:
421157094Srwatson	IPX_LOCK_ASSERT(ipxp);
422157128Srwatson	if (cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || traceallspxs)
42311819Sjulian		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
424139932Srwatson	IPX_UNLOCK(ipxp);
425139932Srwatson	IPX_LIST_UNLOCK();
42611819Sjulian	m_freem(m);
42711819Sjulian}
42811819Sjulian
42911819Sjulian/*
430157094Srwatson * This is structurally similar to the tcp reassembly routine but its
431179408Srwatson * function is somewhat different: it merely queues packets up, and
432157094Srwatson * suppresses duplicates.
43311819Sjulian */
43425652Sjhaystatic int
435157067Srwatsonspx_reass(struct spxpcb *cb, struct spx *si)
43611819Sjulian{
437157067Srwatson	struct spx_q *q;
438157067Srwatson	struct mbuf *m;
439157067Srwatson	struct socket *so = cb->s_ipxpcb->ipxp_socket;
44011819Sjulian	char packetp = cb->s_flags & SF_HI;
44111819Sjulian	int incr;
44211819Sjulian	char wakeup = 0;
44311819Sjulian
444139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
445139932Srwatson
44611819Sjulian	if (si == SI(0))
44711819Sjulian		goto present;
448179408Srwatson
44911819Sjulian	/*
45011819Sjulian	 * Update our news from them.
45111819Sjulian	 */
45211819Sjulian	if (si->si_cc & SPX_SA)
45311819Sjulian		cb->s_flags |= (spx_use_delack ? SF_DELACK : SF_ACKNOW);
45411819Sjulian	if (SSEQ_GT(si->si_alo, cb->s_ralo))
45511819Sjulian		cb->s_flags |= SF_WIN;
45611819Sjulian	if (SSEQ_LEQ(si->si_ack, cb->s_rack)) {
45711819Sjulian		if ((si->si_cc & SPX_SP) && cb->s_rack != (cb->s_smax + 1)) {
45811819Sjulian			spxstat.spxs_rcvdupack++;
459179408Srwatson
46011819Sjulian			/*
461157094Srwatson			 * If this is a completely duplicate ack and other
462157094Srwatson			 * conditions hold, we assume a packet has been
463157094Srwatson			 * dropped and retransmit it exactly as in
464157094Srwatson			 * tcp_input().
46511819Sjulian			 */
46611819Sjulian			if (si->si_ack != cb->s_rack ||
46711819Sjulian			    si->si_alo != cb->s_ralo)
46811819Sjulian				cb->s_dupacks = 0;
46911819Sjulian			else if (++cb->s_dupacks == spxrexmtthresh) {
47011819Sjulian				u_short onxt = cb->s_snxt;
47111819Sjulian				int cwnd = cb->s_cwnd;
47211819Sjulian
47311819Sjulian				cb->s_snxt = si->si_ack;
47411819Sjulian				cb->s_cwnd = CUNIT;
47511819Sjulian				cb->s_force = 1 + SPXT_REXMT;
476139579Srwatson				spx_output(cb, NULL);
47711819Sjulian				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
47811819Sjulian				cb->s_rtt = 0;
47911819Sjulian				if (cwnd >= 4 * CUNIT)
48011819Sjulian					cb->s_cwnd = cwnd / 2;
48111819Sjulian				if (SSEQ_GT(onxt, cb->s_snxt))
48211819Sjulian					cb->s_snxt = onxt;
48311819Sjulian				return (1);
48411819Sjulian			}
48511819Sjulian		} else
48611819Sjulian			cb->s_dupacks = 0;
48711819Sjulian		goto update_window;
48811819Sjulian	}
48911819Sjulian	cb->s_dupacks = 0;
490157094Srwatson
49111819Sjulian	/*
492157094Srwatson	 * If our correspondent acknowledges data we haven't sent TCP would
493157094Srwatson	 * drop the packet after acking.  We'll be a little more permissive.
49411819Sjulian	 */
49511819Sjulian	if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) {
49611819Sjulian		spxstat.spxs_rcvacktoomuch++;
49711819Sjulian		si->si_ack = cb->s_smax + 1;
49811819Sjulian	}
49911819Sjulian	spxstat.spxs_rcvackpack++;
500157094Srwatson
50111819Sjulian	/*
502157094Srwatson	 * If transmit timer is running and timed sequence number was acked,
503157094Srwatson	 * update smoothed round trip time.  See discussion of algorithm in
504157094Srwatson	 * tcp_input.c
50511819Sjulian	 */
50611819Sjulian	if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) {
50711819Sjulian		spxstat.spxs_rttupdated++;
50811819Sjulian		if (cb->s_srtt != 0) {
509157067Srwatson			short delta;
51011819Sjulian			delta = cb->s_rtt - (cb->s_srtt >> 3);
51111819Sjulian			if ((cb->s_srtt += delta) <= 0)
51211819Sjulian				cb->s_srtt = 1;
51311819Sjulian			if (delta < 0)
51411819Sjulian				delta = -delta;
51511819Sjulian			delta -= (cb->s_rttvar >> 2);
51611819Sjulian			if ((cb->s_rttvar += delta) <= 0)
51711819Sjulian				cb->s_rttvar = 1;
51811819Sjulian		} else {
51911819Sjulian			/*
520157094Srwatson			 * No rtt measurement yet.
52111819Sjulian			 */
52211819Sjulian			cb->s_srtt = cb->s_rtt << 3;
52311819Sjulian			cb->s_rttvar = cb->s_rtt << 1;
52411819Sjulian		}
52511819Sjulian		cb->s_rtt = 0;
52611819Sjulian		cb->s_rxtshift = 0;
52711819Sjulian		SPXT_RANGESET(cb->s_rxtcur,
52811819Sjulian			((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
52911819Sjulian			SPXTV_MIN, SPXTV_REXMTMAX);
53011819Sjulian	}
531157094Srwatson
53211819Sjulian	/*
533157094Srwatson	 * If all outstanding data is acked, stop retransmit timer and
534157094Srwatson	 * remember to restart (more output or persist).  If there is more
535157094Srwatson	 * data to be acked, restart retransmit timer, using current
536157094Srwatson	 * (possibly backed-off) value;
53711819Sjulian	 */
53811819Sjulian	if (si->si_ack == cb->s_smax + 1) {
53911819Sjulian		cb->s_timer[SPXT_REXMT] = 0;
54011819Sjulian		cb->s_flags |= SF_RXT;
54111819Sjulian	} else if (cb->s_timer[SPXT_PERSIST] == 0)
54211819Sjulian		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
543157094Srwatson
54411819Sjulian	/*
545157094Srwatson	 * When new data is acked, open the congestion window.  If the window
546157094Srwatson	 * gives us less than ssthresh packets in flight, open exponentially
547157094Srwatson	 * (maxseg at a time).  Otherwise open linearly (maxseg^2 / cwnd at a
548157094Srwatson	 * time).
54911819Sjulian	 */
55011819Sjulian	incr = CUNIT;
55111819Sjulian	if (cb->s_cwnd > cb->s_ssthresh)
55211819Sjulian		incr = max(incr * incr / cb->s_cwnd, 1);
55311819Sjulian	cb->s_cwnd = min(cb->s_cwnd + incr, cb->s_cwmx);
554157094Srwatson
55511819Sjulian	/*
55611819Sjulian	 * Trim Acked data from output queue.
55711819Sjulian	 */
558139589Srwatson	SOCKBUF_LOCK(&so->so_snd);
55911819Sjulian	while ((m = so->so_snd.sb_mb) != NULL) {
56011819Sjulian		if (SSEQ_LT((mtod(m, struct spx *))->si_seq, si->si_ack))
561139589Srwatson			sbdroprecord_locked(&so->so_snd);
56211819Sjulian		else
56311819Sjulian			break;
56411819Sjulian	}
565139589Srwatson	sowwakeup_locked(so);
56611819Sjulian	cb->s_rack = si->si_ack;
56711819Sjulianupdate_window:
56811819Sjulian	if (SSEQ_LT(cb->s_snxt, cb->s_rack))
56911819Sjulian		cb->s_snxt = cb->s_rack;
57043311Sdillon	if (SSEQ_LT(cb->s_swl1, si->si_seq) || ((cb->s_swl1 == si->si_seq &&
57143311Sdillon	    (SSEQ_LT(cb->s_swl2, si->si_ack))) ||
57243305Sdillon	     (cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo)))) {
57311819Sjulian		/* keep track of pure window updates */
57411819Sjulian		if ((si->si_cc & SPX_SP) && cb->s_swl2 == si->si_ack
57511819Sjulian		    && SSEQ_LT(cb->s_ralo, si->si_alo)) {
57611819Sjulian			spxstat.spxs_rcvwinupd++;
57711819Sjulian			spxstat.spxs_rcvdupack--;
57811819Sjulian		}
57911819Sjulian		cb->s_ralo = si->si_alo;
58011819Sjulian		cb->s_swl1 = si->si_seq;
58111819Sjulian		cb->s_swl2 = si->si_ack;
58211819Sjulian		cb->s_swnd = (1 + si->si_alo - si->si_ack);
58311819Sjulian		if (cb->s_swnd > cb->s_smxw)
58411819Sjulian			cb->s_smxw = cb->s_swnd;
58511819Sjulian		cb->s_flags |= SF_WIN;
58611819Sjulian	}
587157094Srwatson
58811819Sjulian	/*
589157094Srwatson	 * If this packet number is higher than that which we have allocated
590157094Srwatson	 * refuse it, unless urgent.
59111819Sjulian	 */
59211819Sjulian	if (SSEQ_GT(si->si_seq, cb->s_alo)) {
59311819Sjulian		if (si->si_cc & SPX_SP) {
59411819Sjulian			spxstat.spxs_rcvwinprobe++;
59511819Sjulian			return (1);
59611819Sjulian		} else
59711819Sjulian			spxstat.spxs_rcvpackafterwin++;
59811819Sjulian		if (si->si_cc & SPX_OB) {
599179410Srwatson			if (SSEQ_GT(si->si_seq, cb->s_alo + 60))
600179410Srwatson				return (1); /* else queue this packet; */
60111819Sjulian		} else {
602139579Srwatson#ifdef BROKEN
603139579Srwatson			/*
604139579Srwatson			 * XXXRW: This is broken on at least one count:
605139579Srwatson			 * spx_close() will free the ipxp and related parts,
606139579Srwatson			 * which are then touched by spx_input() after the
607139579Srwatson			 * return from spx_reass().
608139579Srwatson			 */
609157067Srwatson			/*struct socket *so = cb->s_ipxpcb->ipxp_socket;
61011819Sjulian			if (so->so_state && SS_NOFDREF) {
61125652Sjhay				spx_close(cb);
61297658Stanimura			} else
61397658Stanimura				       would crash system*/
614139579Srwatson#endif
61511819Sjulian			spx_istat.notyet++;
616179410Srwatson			return (1);
61711819Sjulian		}
61811819Sjulian	}
619157094Srwatson
62011819Sjulian	/*
621157094Srwatson	 * If this is a system packet, we don't need to queue it up, and
622157094Srwatson	 * won't update acknowledge #.
62311819Sjulian	 */
624157128Srwatson	if (si->si_cc & SPX_SP)
62511819Sjulian		return (1);
626157094Srwatson
62711819Sjulian	/*
62811819Sjulian	 * We have already seen this packet, so drop.
62911819Sjulian	 */
63011819Sjulian	if (SSEQ_LT(si->si_seq, cb->s_ack)) {
63111819Sjulian		spx_istat.bdreas++;
63211819Sjulian		spxstat.spxs_rcvduppack++;
63311819Sjulian		if (si->si_seq == cb->s_ack - 1)
63411819Sjulian			spx_istat.lstdup++;
63511819Sjulian		return (1);
63611819Sjulian	}
637157094Srwatson
63811819Sjulian	/*
639157094Srwatson	 * Loop through all packets queued up to insert in appropriate
640157094Srwatson	 * sequence.
64111819Sjulian	 */
64225652Sjhay	for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) {
64311819Sjulian		if (si->si_seq == SI(q)->si_seq) {
64411819Sjulian			spxstat.spxs_rcvduppack++;
64511819Sjulian			return (1);
64611819Sjulian		}
64711819Sjulian		if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) {
64811819Sjulian			spxstat.spxs_rcvoopack++;
64911819Sjulian			break;
65011819Sjulian		}
65111819Sjulian	}
65211819Sjulian	insque(si, q->si_prev);
653179408Srwatson
65411819Sjulian	/*
65511819Sjulian	 * If this packet is urgent, inform process
65611819Sjulian	 */
65711819Sjulian	if (si->si_cc & SPX_OB) {
65811819Sjulian		cb->s_iobc = ((char *)si)[1 + sizeof(*si)];
65911819Sjulian		sohasoutofband(so);
66011819Sjulian		cb->s_oobflags |= SF_IOOB;
66111819Sjulian	}
66211819Sjulianpresent:
66311819Sjulian#define SPINC sizeof(struct spxhdr)
664139590Srwatson	SOCKBUF_LOCK(&so->so_rcv);
665157094Srwatson
66611819Sjulian	/*
667157094Srwatson	 * Loop through all packets queued up to update acknowledge number,
668157094Srwatson	 * and present all acknowledged data to user; if in packet interface
669157094Srwatson	 * mode, show packet headers.
67011819Sjulian	 */
67125652Sjhay	for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) {
67211819Sjulian		  if (SI(q)->si_seq == cb->s_ack) {
67311819Sjulian			cb->s_ack++;
67411819Sjulian			m = dtom(q);
67511819Sjulian			if (SI(q)->si_cc & SPX_OB) {
67611819Sjulian				cb->s_oobflags &= ~SF_IOOB;
67711819Sjulian				if (so->so_rcv.sb_cc)
67811819Sjulian					so->so_oobmark = so->so_rcv.sb_cc;
679131031Srwatson				else
680130480Srwatson					so->so_rcv.sb_state |= SBS_RCVATMARK;
68111819Sjulian			}
68211819Sjulian			q = q->si_prev;
68311819Sjulian			remque(q->si_next);
68411819Sjulian			wakeup = 1;
68511819Sjulian			spxstat.spxs_rcvpack++;
68611819Sjulian#ifdef SF_NEWCALL
68711819Sjulian			if (cb->s_flags2 & SF_NEWCALL) {
68811819Sjulian				struct spxhdr *sp = mtod(m, struct spxhdr *);
68911819Sjulian				u_char dt = sp->spx_dt;
69011819Sjulian				spx_newchecks[4]++;
69111819Sjulian				if (dt != cb->s_rhdr.spx_dt) {
69211819Sjulian					struct mbuf *mm =
693111119Simp					   m_getclr(M_DONTWAIT, MT_CONTROL);
69411819Sjulian					spx_newchecks[0]++;
69511819Sjulian					if (mm != NULL) {
69611819Sjulian						u_short *s =
69711819Sjulian							mtod(mm, u_short *);
69811819Sjulian						cb->s_rhdr.spx_dt = dt;
69911819Sjulian						mm->m_len = 5; /*XXX*/
70011819Sjulian						s[0] = 5;
70111819Sjulian						s[1] = 1;
70211819Sjulian						*(u_char *)(&s[2]) = dt;
703139590Srwatson						sbappend_locked(&so->so_rcv, mm);
70411819Sjulian					}
70511819Sjulian				}
70611819Sjulian				if (sp->spx_cc & SPX_OB) {
70711819Sjulian					MCHTYPE(m, MT_OOBDATA);
70811819Sjulian					spx_newchecks[1]++;
70911819Sjulian					so->so_oobmark = 0;
710130480Srwatson					so->so_rcv.sb_state &= ~SBS_RCVATMARK;
71111819Sjulian				}
71211819Sjulian				if (packetp == 0) {
71311819Sjulian					m->m_data += SPINC;
71411819Sjulian					m->m_len -= SPINC;
71511819Sjulian					m->m_pkthdr.len -= SPINC;
71611819Sjulian				}
71711819Sjulian				if ((sp->spx_cc & SPX_EM) || packetp) {
718139590Srwatson					sbappendrecord_locked(&so->so_rcv, m);
71911819Sjulian					spx_newchecks[9]++;
72011819Sjulian				} else
721139590Srwatson					sbappend_locked(&so->so_rcv, m);
72211819Sjulian			} else
72311819Sjulian#endif
724157128Srwatson			if (packetp)
725139590Srwatson				sbappendrecord_locked(&so->so_rcv, m);
726157128Srwatson			else {
72711819Sjulian				cb->s_rhdr = *mtod(m, struct spxhdr *);
72811819Sjulian				m->m_data += SPINC;
72911819Sjulian				m->m_len -= SPINC;
73011819Sjulian				m->m_pkthdr.len -= SPINC;
731139590Srwatson				sbappend_locked(&so->so_rcv, m);
73211819Sjulian			}
73311819Sjulian		  } else
73411819Sjulian			break;
73511819Sjulian	}
73697658Stanimura	if (wakeup)
737139590Srwatson		sorwakeup_locked(so);
738139590Srwatson	else
739139590Srwatson		SOCKBUF_UNLOCK(&so->so_rcv);
74011819Sjulian	return (0);
74111819Sjulian}
74211819Sjulian
74311819Sjulianvoid
744157067Srwatsonspx_ctlinput(int cmd, struct sockaddr *arg_as_sa, void *dummy)
74511819Sjulian{
74611819Sjulian
747157050Srwatson	/* Currently, nothing. */
74811819Sjulian}
74911819Sjulian
75025652Sjhaystatic int
751157067Srwatsonspx_output(struct spxpcb *cb, struct mbuf *m0)
75211819Sjulian{
75311819Sjulian	struct socket *so = cb->s_ipxpcb->ipxp_socket;
754157067Srwatson	struct mbuf *m;
755157067Srwatson	struct spx *si = NULL;
756157067Srwatson	struct sockbuf *sb = &so->so_snd;
75711819Sjulian	int len = 0, win, rcv_win;
75811819Sjulian	short span, off, recordp = 0;
75911819Sjulian	u_short alo;
76011819Sjulian	int error = 0, sendalot;
76111819Sjulian#ifdef notdef
76211819Sjulian	int idle;
76311819Sjulian#endif
76411819Sjulian	struct mbuf *mprev;
76511819Sjulian
766139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
767139932Srwatson
76825652Sjhay	if (m0 != NULL) {
76911819Sjulian		int mtu = cb->s_mtu;
77011819Sjulian		int datalen;
771157094Srwatson
77211819Sjulian		/*
77311819Sjulian		 * Make sure that packet isn't too big.
77411819Sjulian		 */
77525652Sjhay		for (m = m0; m != NULL; m = m->m_next) {
77611819Sjulian			mprev = m;
77711819Sjulian			len += m->m_len;
77811819Sjulian			if (m->m_flags & M_EOR)
77911819Sjulian				recordp = 1;
78011819Sjulian		}
78111819Sjulian		datalen = (cb->s_flags & SF_HO) ?
78225652Sjhay				len - sizeof(struct spxhdr) : len;
78311819Sjulian		if (datalen > mtu) {
78411819Sjulian			if (cb->s_flags & SF_PI) {
78511819Sjulian				m_freem(m0);
78611819Sjulian				return (EMSGSIZE);
78711819Sjulian			} else {
78811819Sjulian				int oldEM = cb->s_cc & SPX_EM;
78911819Sjulian
79011819Sjulian				cb->s_cc &= ~SPX_EM;
79111819Sjulian				while (len > mtu) {
792157167Srwatson					m = m_copym(m0, 0, mtu, M_DONTWAIT);
793157167Srwatson					if (m == NULL) {
794157167Srwatson					    cb->s_cc |= oldEM;
795157167Srwatson					    m_freem(m0);
796157167Srwatson					    return (ENOBUFS);
797157167Srwatson					}
79811819Sjulian					if (cb->s_flags & SF_NEWCALL) {
79911819Sjulian					    struct mbuf *mm = m;
80011819Sjulian					    spx_newchecks[7]++;
80125652Sjhay					    while (mm != NULL) {
80211819Sjulian						mm->m_flags &= ~M_EOR;
80311819Sjulian						mm = mm->m_next;
80411819Sjulian					    }
80511819Sjulian					}
80611819Sjulian					error = spx_output(cb, m);
80711819Sjulian					if (error) {
80811819Sjulian						cb->s_cc |= oldEM;
80911819Sjulian						m_freem(m0);
81025652Sjhay						return (error);
81111819Sjulian					}
81211819Sjulian					m_adj(m0, mtu);
81311819Sjulian					len -= mtu;
81411819Sjulian				}
81511819Sjulian				cb->s_cc |= oldEM;
81611819Sjulian			}
81711819Sjulian		}
818157094Srwatson
81911819Sjulian		/*
82011819Sjulian		 * Force length even, by adding a "garbage byte" if
82111819Sjulian		 * necessary.
82211819Sjulian		 */
82311819Sjulian		if (len & 1) {
82411819Sjulian			m = mprev;
82511819Sjulian			if (M_TRAILINGSPACE(m) >= 1)
82611819Sjulian				m->m_len++;
82711819Sjulian			else {
828111119Simp				struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
82911819Sjulian
83025652Sjhay				if (m1 == NULL) {
83111819Sjulian					m_freem(m0);
83211819Sjulian					return (ENOBUFS);
83311819Sjulian				}
83411819Sjulian				m1->m_len = 1;
83511819Sjulian				*(mtod(m1, u_char *)) = 0;
83611819Sjulian				m->m_next = m1;
83711819Sjulian			}
83811819Sjulian		}
839151967Sandre		m = m_gethdr(M_DONTWAIT, MT_DATA);
84025652Sjhay		if (m == NULL) {
84111819Sjulian			m_freem(m0);
84211819Sjulian			return (ENOBUFS);
84311819Sjulian		}
844157094Srwatson
84511819Sjulian		/*
846157094Srwatson		 * Fill in mbuf with extended SP header and addresses and
847157094Srwatson		 * length put into network format.
84811819Sjulian		 */
84925652Sjhay		MH_ALIGN(m, sizeof(struct spx));
85025652Sjhay		m->m_len = sizeof(struct spx);
85111819Sjulian		m->m_next = m0;
85211819Sjulian		si = mtod(m, struct spx *);
85311819Sjulian		si->si_i = *cb->s_ipx;
85411819Sjulian		si->si_s = cb->s_shdr;
85511819Sjulian		if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) {
856157067Srwatson			struct spxhdr *sh;
85725652Sjhay			if (m0->m_len < sizeof(*sh)) {
85811819Sjulian				if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) {
85925652Sjhay					m_free(m);
86011819Sjulian					m_freem(m0);
86111819Sjulian					return (EINVAL);
86211819Sjulian				}
86311819Sjulian				m->m_next = m0;
86411819Sjulian			}
86511819Sjulian			sh = mtod(m0, struct spxhdr *);
86611819Sjulian			si->si_dt = sh->spx_dt;
86711819Sjulian			si->si_cc |= sh->spx_cc & SPX_EM;
86825652Sjhay			m0->m_len -= sizeof(*sh);
86925652Sjhay			m0->m_data += sizeof(*sh);
87025652Sjhay			len -= sizeof(*sh);
87111819Sjulian		}
87211819Sjulian		len += sizeof(*si);
87311819Sjulian		if ((cb->s_flags2 & SF_NEWCALL) && recordp) {
87425652Sjhay			si->si_cc |= SPX_EM;
87511819Sjulian			spx_newchecks[8]++;
87611819Sjulian		}
87711819Sjulian		if (cb->s_oobflags & SF_SOOB) {
87811819Sjulian			/*
879157094Srwatson			 * Per jqj@cornell: Make sure OB packets convey
880157094Srwatson			 * exactly 1 byte.  If the packet is 1 byte or
881157094Srwatson			 * larger, we have already guaranted there to be at
882157094Srwatson			 * least one garbage byte for the checksum, and extra
883157094Srwatson			 * bytes shouldn't hurt!
88411819Sjulian			 */
88511819Sjulian			if (len > sizeof(*si)) {
88611819Sjulian				si->si_cc |= SPX_OB;
88711819Sjulian				len = (1 + sizeof(*si));
88811819Sjulian			}
88911819Sjulian		}
89011819Sjulian		si->si_len = htons((u_short)len);
89111819Sjulian		m->m_pkthdr.len = ((len - 1) | 1) + 1;
892157094Srwatson
89311819Sjulian		/*
894157094Srwatson		 * Queue stuff up for output.
89511819Sjulian		 */
89611819Sjulian		sbappendrecord(sb, m);
89711819Sjulian		cb->s_seq++;
89811819Sjulian	}
89911819Sjulian#ifdef notdef
90011819Sjulian	idle = (cb->s_smax == (cb->s_rack - 1));
90111819Sjulian#endif
90211819Sjulianagain:
90311819Sjulian	sendalot = 0;
90411819Sjulian	off = cb->s_snxt - cb->s_rack;
90525652Sjhay	win = min(cb->s_swnd, (cb->s_cwnd / CUNIT));
90611819Sjulian
90711819Sjulian	/*
908157094Srwatson	 * If in persist timeout with window of 0, send a probe.  Otherwise,
909179408Srwatson	 * if window is small but non-zero and timer expired, send what we
910179408Srwatson	 * can and go into transmit state.
91111819Sjulian	 */
91211819Sjulian	if (cb->s_force == 1 + SPXT_PERSIST) {
91311819Sjulian		if (win != 0) {
91411819Sjulian			cb->s_timer[SPXT_PERSIST] = 0;
91511819Sjulian			cb->s_rxtshift = 0;
91611819Sjulian		}
91711819Sjulian	}
91811819Sjulian	span = cb->s_seq - cb->s_rack;
91911819Sjulian	len = min(span, win) - off;
92011819Sjulian
92111819Sjulian	if (len < 0) {
92211819Sjulian		/*
923157094Srwatson		 * Window shrank after we went into it.  If window shrank to
924157094Srwatson		 * 0, cancel pending restransmission and pull s_snxt back to
925157094Srwatson		 * (closed) window.  We will enter persist state below.  If
926157094Srwatson		 * the widndow didn't close completely, just wait for an ACK.
92711819Sjulian		 */
92811819Sjulian		len = 0;
92911819Sjulian		if (win == 0) {
93011819Sjulian			cb->s_timer[SPXT_REXMT] = 0;
93111819Sjulian			cb->s_snxt = cb->s_rack;
93211819Sjulian		}
93311819Sjulian	}
93411819Sjulian	if (len > 1)
93511819Sjulian		sendalot = 1;
93611819Sjulian	rcv_win = sbspace(&so->so_rcv);
93711819Sjulian
93811819Sjulian	/*
93911819Sjulian	 * Send if we owe peer an ACK.
94011819Sjulian	 */
94111819Sjulian	if (cb->s_oobflags & SF_SOOB) {
94211819Sjulian		/*
943157094Srwatson		 * Must transmit this out of band packet.
94411819Sjulian		 */
94511819Sjulian		cb->s_oobflags &= ~ SF_SOOB;
94611819Sjulian		sendalot = 1;
94711819Sjulian		spxstat.spxs_sndurg++;
94811819Sjulian		goto found;
94911819Sjulian	}
95011819Sjulian	if (cb->s_flags & SF_ACKNOW)
95111819Sjulian		goto send;
95211819Sjulian	if (cb->s_state < TCPS_ESTABLISHED)
95311819Sjulian		goto send;
954157094Srwatson
95511819Sjulian	/*
956157094Srwatson	 * Silly window can't happen in spx.  Code from TCP deleted.
95711819Sjulian	 */
95811819Sjulian	if (len)
95911819Sjulian		goto send;
960157094Srwatson
96111819Sjulian	/*
962157094Srwatson	 * Compare available window to amount of window known to peer (as
963157094Srwatson	 * advertised window less next expected input.)  If the difference is
964157094Srwatson	 * at least two packets or at least 35% of the mximum possible
965157094Srwatson	 * window, then want to send a window update to peer.
96611819Sjulian	 */
96711819Sjulian	if (rcv_win > 0) {
96811819Sjulian		u_short delta =  1 + cb->s_alo - cb->s_ack;
96911819Sjulian		int adv = rcv_win - (delta * cb->s_mtu);
970139584Srwatson
97111819Sjulian		if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) ||
97211819Sjulian		    (100 * adv / so->so_rcv.sb_hiwat >= 35)) {
97311819Sjulian			spxstat.spxs_sndwinup++;
97411819Sjulian			cb->s_flags |= SF_ACKNOW;
97511819Sjulian			goto send;
97611819Sjulian		}
97711819Sjulian
97811819Sjulian	}
979157094Srwatson
98011819Sjulian	/*
981157094Srwatson	 * Many comments from tcp_output.c are appropriate here including ...
98211819Sjulian	 * If send window is too small, there is data to transmit, and no
983157094Srwatson	 * retransmit or persist is pending, then go to persist state.  If
984157094Srwatson	 * nothing happens soon, send when timer expires: if window is
985179408Srwatson	 * non-zero, transmit what we can, otherwise send a probe.
98611819Sjulian	 */
98711819Sjulian	if (so->so_snd.sb_cc && cb->s_timer[SPXT_REXMT] == 0 &&
988157128Srwatson	    cb->s_timer[SPXT_PERSIST] == 0) {
989157128Srwatson		cb->s_rxtshift = 0;
990157128Srwatson		spx_setpersist(cb);
99111819Sjulian	}
992157094Srwatson
99311819Sjulian	/*
99411819Sjulian	 * No reason to send a packet, just return.
99511819Sjulian	 */
99611819Sjulian	cb->s_outx = 1;
99711819Sjulian	return (0);
99811819Sjulian
99911819Sjuliansend:
100011819Sjulian	/*
100111819Sjulian	 * Find requested packet.
100211819Sjulian	 */
100311819Sjulian	si = 0;
100411819Sjulian	if (len > 0) {
100511819Sjulian		cb->s_want = cb->s_snxt;
100625652Sjhay		for (m = sb->sb_mb; m != NULL; m = m->m_act) {
100711819Sjulian			si = mtod(m, struct spx *);
100811819Sjulian			if (SSEQ_LEQ(cb->s_snxt, si->si_seq))
100911819Sjulian				break;
101011819Sjulian		}
101111819Sjulian	found:
101225652Sjhay		if (si != NULL) {
101311819Sjulian			if (si->si_seq == cb->s_snxt)
101411819Sjulian					cb->s_snxt++;
101511819Sjulian				else
101611819Sjulian					spxstat.spxs_sndvoid++, si = 0;
101711819Sjulian		}
101811819Sjulian	}
1019157094Srwatson
102011819Sjulian	/*
1021157094Srwatson	 * Update window.
102211819Sjulian	 */
102311819Sjulian	if (rcv_win < 0)
102411819Sjulian		rcv_win = 0;
102511819Sjulian	alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu));
1026139584Srwatson	if (SSEQ_LT(alo, cb->s_alo))
102711819Sjulian		alo = cb->s_alo;
102811819Sjulian
102925652Sjhay	if (si != NULL) {
103011819Sjulian		/*
1031157094Srwatson		 * Must make a copy of this packet for ipx_output to monkey
1032157094Srwatson		 * with.
103311819Sjulian		 */
103411819Sjulian		m = m_copy(dtom(si), 0, (int)M_COPYALL);
1035157094Srwatson		if (m == NULL)
103611819Sjulian			return (ENOBUFS);
103711819Sjulian		si = mtod(m, struct spx *);
103811819Sjulian		if (SSEQ_LT(si->si_seq, cb->s_smax))
103911819Sjulian			spxstat.spxs_sndrexmitpack++;
104011819Sjulian		else
104111819Sjulian			spxstat.spxs_sndpack++;
104211819Sjulian	} else if (cb->s_force || cb->s_flags & SF_ACKNOW) {
104311819Sjulian		/*
1044157094Srwatson		 * Must send an acknowledgement or a probe.
104511819Sjulian		 */
104611819Sjulian		if (cb->s_force)
104711819Sjulian			spxstat.spxs_sndprobe++;
104811819Sjulian		if (cb->s_flags & SF_ACKNOW)
104911819Sjulian			spxstat.spxs_sndacks++;
1050151967Sandre		m = m_gethdr(M_DONTWAIT, MT_DATA);
105125652Sjhay		if (m == NULL)
105211819Sjulian			return (ENOBUFS);
1053157094Srwatson
105411819Sjulian		/*
1055157094Srwatson		 * Fill in mbuf with extended SP header and addresses and
1056157094Srwatson		 * length put into network format.
105711819Sjulian		 */
105825652Sjhay		MH_ALIGN(m, sizeof(struct spx));
105925652Sjhay		m->m_len = sizeof(*si);
106025652Sjhay		m->m_pkthdr.len = sizeof(*si);
106111819Sjulian		si = mtod(m, struct spx *);
106211819Sjulian		si->si_i = *cb->s_ipx;
106311819Sjulian		si->si_s = cb->s_shdr;
106411819Sjulian		si->si_seq = cb->s_smax + 1;
106525652Sjhay		si->si_len = htons(sizeof(*si));
106611819Sjulian		si->si_cc |= SPX_SP;
106711819Sjulian	} else {
106811819Sjulian		cb->s_outx = 3;
106997658Stanimura		if (so->so_options & SO_DEBUG || traceallspxs)
107011819Sjulian			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
107111819Sjulian		return (0);
107211819Sjulian	}
1073179408Srwatson
107411819Sjulian	/*
107511819Sjulian	 * Stuff checksum and output datagram.
107611819Sjulian	 */
107711819Sjulian	if ((si->si_cc & SPX_SP) == 0) {
107811819Sjulian		if (cb->s_force != (1 + SPXT_PERSIST) ||
107911819Sjulian		    cb->s_timer[SPXT_PERSIST] == 0) {
108011819Sjulian			/*
1081139584Srwatson			 * If this is a new packet and we are not currently
108211819Sjulian			 * timing anything, time this one.
108311819Sjulian			 */
108411819Sjulian			if (SSEQ_LT(cb->s_smax, si->si_seq)) {
108511819Sjulian				cb->s_smax = si->si_seq;
108611819Sjulian				if (cb->s_rtt == 0) {
108711819Sjulian					spxstat.spxs_segstimed++;
108811819Sjulian					cb->s_rtseq = si->si_seq;
108911819Sjulian					cb->s_rtt = 1;
109011819Sjulian				}
109111819Sjulian			}
1092157094Srwatson
109311819Sjulian			/*
1094157094Srwatson			 * Set rexmt timer if not currently set, initial
1095157094Srwatson			 * value for retransmit timer is smoothed round-trip
1096157094Srwatson			 * time + 2 * round-trip time variance.  Initialize
1097157094Srwatson			 * shift counter which is used for backoff of
1098157094Srwatson			 * retransmit time.
109911819Sjulian			 */
110011819Sjulian			if (cb->s_timer[SPXT_REXMT] == 0 &&
110111819Sjulian			    cb->s_snxt != cb->s_rack) {
110211819Sjulian				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
110311819Sjulian				if (cb->s_timer[SPXT_PERSIST]) {
110411819Sjulian					cb->s_timer[SPXT_PERSIST] = 0;
110511819Sjulian					cb->s_rxtshift = 0;
110611819Sjulian				}
110711819Sjulian			}
1108157094Srwatson		} else if (SSEQ_LT(cb->s_smax, si->si_seq))
110911819Sjulian			cb->s_smax = si->si_seq;
111011819Sjulian	} else if (cb->s_state < TCPS_ESTABLISHED) {
111111819Sjulian		if (cb->s_rtt == 0)
111211819Sjulian			cb->s_rtt = 1; /* Time initial handshake */
111311819Sjulian		if (cb->s_timer[SPXT_REXMT] == 0)
111411819Sjulian			cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
111511819Sjulian	}
111611819Sjulian
1117157094Srwatson	/*
1118157094Srwatson	 * Do not request acks when we ack their data packets or when we do a
1119157094Srwatson	 * gratuitous window update.
1120157094Srwatson	 */
1121157094Srwatson	if (((si->si_cc & SPX_SP) == 0) || cb->s_force)
1122157094Srwatson		si->si_cc |= SPX_SA;
1123157094Srwatson	si->si_seq = htons(si->si_seq);
1124157094Srwatson	si->si_alo = htons(alo);
1125157094Srwatson	si->si_ack = htons(cb->s_ack);
112611819Sjulian
1127157094Srwatson	if (ipxcksum)
1128157094Srwatson		si->si_sum = ipx_cksum(m, ntohs(si->si_len));
1129157094Srwatson	else
1130157094Srwatson		si->si_sum = 0xffff;
113111819Sjulian
1132157094Srwatson	cb->s_outx = 4;
1133157094Srwatson	if (so->so_options & SO_DEBUG || traceallspxs)
1134157094Srwatson		spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
1135157094Srwatson
1136157094Srwatson	if (so->so_options & SO_DONTROUTE)
1137157094Srwatson		error = ipx_outputfl(m, NULL, IPX_ROUTETOIF);
1138157094Srwatson	else
1139157094Srwatson		error = ipx_outputfl(m, &cb->s_ipxpcb->ipxp_route, 0);
1140157094Srwatson	if (error)
114111819Sjulian		return (error);
114211819Sjulian	spxstat.spxs_sndtotal++;
1143157094Srwatson
114411819Sjulian	/*
1145157094Srwatson	 * Data sent (as far as we can tell).  If this advertises a larger
1146157094Srwatson	 * window than any other segment, then remember the size of the
1147157094Srwatson	 * advertized window.  Any pending ACK has now been sent.
114811819Sjulian	 */
114911819Sjulian	cb->s_force = 0;
115011819Sjulian	cb->s_flags &= ~(SF_ACKNOW|SF_DELACK);
115111819Sjulian	if (SSEQ_GT(alo, cb->s_alo))
115211819Sjulian		cb->s_alo = alo;
115311819Sjulian	if (sendalot)
115411819Sjulian		goto again;
115511819Sjulian	cb->s_outx = 5;
115611819Sjulian	return (0);
115711819Sjulian}
115811819Sjulian
115933181Seivindstatic int spx_do_persist_panics = 0;
116011819Sjulian
116125652Sjhaystatic void
1162157067Srwatsonspx_setpersist(struct spxpcb *cb)
116311819Sjulian{
1164157067Srwatson	int t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
116511819Sjulian
1166139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1167139932Srwatson
116811819Sjulian	if (cb->s_timer[SPXT_REXMT] && spx_do_persist_panics)
116911819Sjulian		panic("spx_output REXMT");
1170157094Srwatson
117111819Sjulian	/*
117211819Sjulian	 * Start/restart persistance timer.
117311819Sjulian	 */
117411819Sjulian	SPXT_RANGESET(cb->s_timer[SPXT_PERSIST],
117511819Sjulian	    t*spx_backoff[cb->s_rxtshift],
117611819Sjulian	    SPXTV_PERSMIN, SPXTV_PERSMAX);
117711819Sjulian	if (cb->s_rxtshift < SPX_MAXRXTSHIFT)
117811819Sjulian		cb->s_rxtshift++;
117911819Sjulian}
118025652Sjhay
118111819Sjulianint
1182157067Srwatsonspx_ctloutput(struct socket *so, struct sockopt *sopt)
118311819Sjulian{
1184157125Srwatson	struct spxhdr spxhdr;
1185157125Srwatson	struct ipxpcb *ipxp;
1186157067Srwatson	struct spxpcb *cb;
118738482Swollman	int mask, error;
118838482Swollman	short soptval;
118938482Swollman	u_short usoptval;
119038482Swollman	int optval;
119111819Sjulian
1192157128Srwatson	ipxp = sotoipxpcb(so);
1193157128Srwatson	KASSERT(ipxp != NULL, ("spx_ctloutput: ipxp == NULL"));
1194157128Srwatson
1195157094Srwatson	/*
1196157094Srwatson	 * This will have to be changed when we do more general stacking of
1197157094Srwatson	 * protocols.
1198157094Srwatson	 */
1199157094Srwatson	if (sopt->sopt_level != IPXPROTO_SPX)
120038482Swollman		return (ipx_ctloutput(so, sopt));
1201157125Srwatson
1202157128Srwatson	IPX_LOCK(ipxp);
1203157128Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1204157128Srwatson		IPX_UNLOCK(ipxp);
1205157128Srwatson		return (ECONNRESET);
1206157128Srwatson	}
1207157125Srwatson
1208157125Srwatson	IPX_LOCK(ipxp);
1209157125Srwatson	cb = ipxtospxpcb(ipxp);
1210157094Srwatson	KASSERT(cb != NULL, ("spx_ctloutput: cb == NULL"));
121111819Sjulian
1212157125Srwatson	error = 0;
121338482Swollman	switch (sopt->sopt_dir) {
121438482Swollman	case SOPT_GET:
121538482Swollman		switch (sopt->sopt_name) {
121611819Sjulian		case SO_HEADERS_ON_INPUT:
121711819Sjulian			mask = SF_HI;
121811819Sjulian			goto get_flags;
121911819Sjulian
122011819Sjulian		case SO_HEADERS_ON_OUTPUT:
122111819Sjulian			mask = SF_HO;
122211819Sjulian		get_flags:
122338482Swollman			soptval = cb->s_flags & mask;
1224157125Srwatson			IPX_UNLOCK(ipxp);
1225157125Srwatson			error = sooptcopyout(sopt, &soptval,
1226157125Srwatson			    sizeof(soptval));
122711819Sjulian			break;
122811819Sjulian
122911819Sjulian		case SO_MTU:
123038482Swollman			usoptval = cb->s_mtu;
1231157125Srwatson			IPX_UNLOCK(ipxp);
1232157125Srwatson			error = sooptcopyout(sopt, &usoptval,
1233157125Srwatson			    sizeof(usoptval));
123411819Sjulian			break;
123511819Sjulian
123611819Sjulian		case SO_LAST_HEADER:
1237157125Srwatson			spxhdr = cb->s_rhdr;
1238157125Srwatson			IPX_UNLOCK(ipxp);
1239157125Srwatson			error = sooptcopyout(sopt, &spxhdr, sizeof(spxhdr));
124011819Sjulian			break;
124111819Sjulian
124211819Sjulian		case SO_DEFAULT_HEADERS:
1243157125Srwatson			spxhdr = cb->s_shdr;
1244157125Srwatson			IPX_UNLOCK(ipxp);
1245157125Srwatson			error = sooptcopyout(sopt, &spxhdr, sizeof(spxhdr));
124611819Sjulian			break;
124711819Sjulian
124811819Sjulian		default:
1249157125Srwatson			IPX_UNLOCK(ipxp);
125038482Swollman			error = ENOPROTOOPT;
125111819Sjulian		}
125211819Sjulian		break;
125311819Sjulian
125438482Swollman	case SOPT_SET:
1255157094Srwatson		/*
1256157094Srwatson		 * XXX Why are these shorts on get and ints on set?  That
1257157094Srwatson		 * doesn't make any sense...
1258157128Srwatson		 *
1259157128Srwatson		 * XXXRW: Note, when we re-acquire the ipxp lock, we should
1260157128Srwatson		 * re-check that it's not dropped.
1261157094Srwatson		 */
1262157125Srwatson		IPX_UNLOCK(ipxp);
126338482Swollman		switch (sopt->sopt_name) {
126411819Sjulian		case SO_HEADERS_ON_INPUT:
126511819Sjulian			mask = SF_HI;
126611819Sjulian			goto set_head;
126711819Sjulian
126811819Sjulian		case SO_HEADERS_ON_OUTPUT:
126911819Sjulian			mask = SF_HO;
127011819Sjulian		set_head:
127138482Swollman			error = sooptcopyin(sopt, &optval, sizeof optval,
127238482Swollman					    sizeof optval);
127338482Swollman			if (error)
127438482Swollman				break;
127538482Swollman
1276139932Srwatson			IPX_LOCK(ipxp);
127711819Sjulian			if (cb->s_flags & SF_PI) {
127838482Swollman				if (optval)
127911819Sjulian					cb->s_flags |= mask;
128011819Sjulian				else
128111819Sjulian					cb->s_flags &= ~mask;
128211819Sjulian			} else error = EINVAL;
1283139932Srwatson			IPX_UNLOCK(ipxp);
128411819Sjulian			break;
128511819Sjulian
128611819Sjulian		case SO_MTU:
128738482Swollman			error = sooptcopyin(sopt, &usoptval, sizeof usoptval,
128838482Swollman					    sizeof usoptval);
128938482Swollman			if (error)
129038482Swollman				break;
1291139932Srwatson			/* Unlocked write. */
129238482Swollman			cb->s_mtu = usoptval;
129311819Sjulian			break;
129411819Sjulian
129511819Sjulian#ifdef SF_NEWCALL
129611819Sjulian		case SO_NEWCALL:
129738482Swollman			error = sooptcopyin(sopt, &optval, sizeof optval,
129838482Swollman					    sizeof optval);
129938482Swollman			if (error)
130038482Swollman				break;
1301139932Srwatson			IPX_LOCK(ipxp);
130238482Swollman			if (optval) {
130311819Sjulian				cb->s_flags2 |= SF_NEWCALL;
130411819Sjulian				spx_newchecks[5]++;
130511819Sjulian			} else {
130611819Sjulian				cb->s_flags2 &= ~SF_NEWCALL;
130711819Sjulian				spx_newchecks[6]++;
130811819Sjulian			}
1309139932Srwatson			IPX_UNLOCK(ipxp);
131011819Sjulian			break;
131111819Sjulian#endif
131211819Sjulian
131311819Sjulian		case SO_DEFAULT_HEADERS:
131411819Sjulian			{
131538482Swollman				struct spxhdr sp;
131638482Swollman
131738482Swollman				error = sooptcopyin(sopt, &sp, sizeof sp,
131838482Swollman						    sizeof sp);
131938482Swollman				if (error)
132038482Swollman					break;
1321139932Srwatson				IPX_LOCK(ipxp);
132238482Swollman				cb->s_dt = sp.spx_dt;
132338482Swollman				cb->s_cc = sp.spx_cc & SPX_EM;
1324139932Srwatson				IPX_UNLOCK(ipxp);
132511819Sjulian			}
132611819Sjulian			break;
132711819Sjulian
132811819Sjulian		default:
132938482Swollman			error = ENOPROTOOPT;
133011819Sjulian		}
133111819Sjulian		break;
1332157125Srwatson
1333157125Srwatson	default:
1334157125Srwatson		panic("spx_ctloutput: bad socket option direction");
133511819Sjulian	}
133638482Swollman	return (error);
133711819Sjulian}
133811819Sjulian
1339157366Srwatsonstatic void
1340157067Srwatsonspx_usr_abort(struct socket *so)
134111819Sjulian{
134224659Sjhay	struct ipxpcb *ipxp;
134324659Sjhay	struct spxpcb *cb;
134411819Sjulian
134524659Sjhay	ipxp = sotoipxpcb(so);
1346157094Srwatson	KASSERT(ipxp != NULL, ("spx_usr_abort: ipxp == NULL"));
1347157094Srwatson
134824659Sjhay	cb = ipxtospxpcb(ipxp);
1349157094Srwatson	KASSERT(cb != NULL, ("spx_usr_abort: cb == NULL"));
135011819Sjulian
1351139932Srwatson	IPX_LIST_LOCK();
1352139932Srwatson	IPX_LOCK(ipxp);
135324659Sjhay	spx_drop(cb, ECONNABORTED);
1354160549Srwatson	IPX_UNLOCK(ipxp);
1355139932Srwatson	IPX_LIST_UNLOCK();
135624659Sjhay}
135711819Sjulian
135824659Sjhay/*
1359157094Srwatson * Accept a connection.  Essentially all the work is done at higher levels;
1360157094Srwatson * just return the address of the peer, storing through addr.
136124659Sjhay */
136224659Sjhaystatic int
1363157067Srwatsonspx_accept(struct socket *so, struct sockaddr **nam)
136424659Sjhay{
136524659Sjhay	struct ipxpcb *ipxp;
136628270Swollman	struct sockaddr_ipx *sipx, ssipx;
136711819Sjulian
136824659Sjhay	ipxp = sotoipxpcb(so);
1369157154Srwatson	KASSERT(ipxp != NULL, ("spx_accept: ipxp == NULL"));
1370157094Srwatson
137128270Swollman	sipx = &ssipx;
137228270Swollman	bzero(sipx, sizeof *sipx);
137328270Swollman	sipx->sipx_len = sizeof *sipx;
137424659Sjhay	sipx->sipx_family = AF_IPX;
1375139932Srwatson	IPX_LOCK(ipxp);
137624659Sjhay	sipx->sipx_addr = ipxp->ipxp_faddr;
1377139932Srwatson	IPX_UNLOCK(ipxp);
1378139932Srwatson	*nam = sodupsockaddr((struct sockaddr *)sipx, M_WAITOK);
137924659Sjhay	return (0);
138024659Sjhay}
138124659Sjhay
138224659Sjhaystatic int
1383157067Srwatsonspx_attach(struct socket *so, int proto, struct thread *td)
138424659Sjhay{
138524659Sjhay	struct ipxpcb *ipxp;
138624659Sjhay	struct spxpcb *cb;
138724659Sjhay	struct mbuf *mm;
138824659Sjhay	struct sockbuf *sb;
1389139932Srwatson	int error;
139024659Sjhay
139124659Sjhay	ipxp = sotoipxpcb(so);
1392157094Srwatson	KASSERT(ipxp == NULL, ("spx_attach: ipxp != NULL"));
139324659Sjhay
139424659Sjhay	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
139524659Sjhay		error = soreserve(so, (u_long) 3072, (u_long) 3072);
139611819Sjulian		if (error)
1397157127Srwatson			return (error);
139824659Sjhay	}
139911819Sjulian
140069781Sdwmalone	MALLOC(cb, struct spxpcb *, sizeof *cb, M_PCB, M_NOWAIT | M_ZERO);
1401157127Srwatson	if (cb == NULL)
1402157127Srwatson		return (ENOBUFS);
1403151967Sandre	mm = m_getclr(M_DONTWAIT, MT_DATA);
140424659Sjhay	if (mm == NULL) {
140528270Swollman		FREE(cb, M_PCB);
1406157127Srwatson		return (ENOBUFS);
140724659Sjhay	}
1408157127Srwatson
1409157127Srwatson	IPX_LIST_LOCK();
1410157127Srwatson	error = ipx_pcballoc(so, &ipxpcb_list, td);
1411157127Srwatson	if (error) {
1412157127Srwatson		IPX_LIST_UNLOCK();
1413157127Srwatson		m_free(mm);
1414157127Srwatson		FREE(cb, M_PCB);
1415157127Srwatson		return (error);
1416157127Srwatson	}
1417157127Srwatson	ipxp = sotoipxpcb(so);
1418157145Srwatson	ipxp->ipxp_flags |= IPXP_SPX;
1419157127Srwatson
142024659Sjhay	cb->s_ipx = mtod(mm, struct ipx *);
142124659Sjhay	cb->s_state = TCPS_LISTEN;
142224659Sjhay	cb->s_smax = -1;
142324659Sjhay	cb->s_swl1 = -1;
142424659Sjhay	cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q;
142524659Sjhay	cb->s_ipxpcb = ipxp;
142625652Sjhay	cb->s_mtu = 576 - sizeof(struct spx);
1427157094Srwatson	sb = &so->so_snd;
142824659Sjhay	cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu;
142924659Sjhay	cb->s_ssthresh = cb->s_cwnd;
143025652Sjhay	cb->s_cwmx = sbspace(sb) * CUNIT / (2 * sizeof(struct spx));
1431179408Srwatson
1432157094Srwatson	/*
1433157094Srwatson	 * Above is recomputed when connecting to account for changed
1434157094Srwatson	 * buffering or mtu's.
1435157094Srwatson	 */
143624659Sjhay	cb->s_rtt = SPXTV_SRTTBASE;
143724659Sjhay	cb->s_rttvar = SPXTV_SRTTDFLT << 2;
143824659Sjhay	SPXT_RANGESET(cb->s_rxtcur,
143924659Sjhay	    ((SPXTV_SRTTBASE >> 2) + (SPXTV_SRTTDFLT << 2)) >> 1,
144024659Sjhay	    SPXTV_MIN, SPXTV_REXMTMAX);
1441139584Srwatson	ipxp->ipxp_pcb = (caddr_t)cb;
1442139932Srwatson	IPX_LIST_UNLOCK();
1443157128Srwatson	return (0);
144424659Sjhay}
144511819Sjulian
1446157128Srwatsonstatic void
1447157128Srwatsonspx_pcbdetach(struct ipxpcb *ipxp)
1448157128Srwatson{
1449157128Srwatson	struct spxpcb *cb;
1450157128Srwatson	struct spx_q *s;
1451157128Srwatson	struct mbuf *m;
1452157128Srwatson
1453157128Srwatson	IPX_LOCK_ASSERT(ipxp);
1454157128Srwatson
1455157128Srwatson	cb = ipxtospxpcb(ipxp);
1456157128Srwatson	KASSERT(cb != NULL, ("spx_pcbdetach: cb == NULL"));
1457157128Srwatson
1458157140Srwatson	s = cb->s_q.si_next;
1459157140Srwatson	while (s != &(cb->s_q)) {
1460157140Srwatson		s = s->si_next;
1461157128Srwatson		remque(s);
1462157128Srwatson		m = dtom(s);
1463157128Srwatson		m_freem(m);
1464157128Srwatson	}
1465157128Srwatson	m_free(dtom(cb->s_ipx));
1466157128Srwatson	FREE(cb, M_PCB);
1467157128Srwatson	ipxp->ipxp_pcb = NULL;
1468157128Srwatson}
1469157128Srwatson
147024659Sjhaystatic int
1471157067Srwatsonspx_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
1472139584Srwatson{
147324659Sjhay	struct ipxpcb *ipxp;
1474139932Srwatson	int error;
147511819Sjulian
147624659Sjhay	ipxp = sotoipxpcb(so);
1477157094Srwatson	KASSERT(ipxp != NULL, ("spx_bind: ipxp == NULL"));
147811819Sjulian
1479139932Srwatson	IPX_LIST_LOCK();
1480139932Srwatson	IPX_LOCK(ipxp);
1481157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1482157153Srwatson		error = EINVAL;
1483157153Srwatson		goto out;
1484157153Srwatson	}
1485139932Srwatson	error = ipx_pcbbind(ipxp, nam, td);
1486157153Srwatsonout:
1487139932Srwatson	IPX_UNLOCK(ipxp);
1488139932Srwatson	IPX_LIST_UNLOCK();
1489139932Srwatson	return (error);
1490139584Srwatson}
1491139584Srwatson
1492160549Srwatsonstatic void
1493160549Srwatsonspx_usr_close(struct socket *so)
1494160549Srwatson{
1495160549Srwatson	struct ipxpcb *ipxp;
1496160549Srwatson	struct spxpcb *cb;
1497160549Srwatson
1498160549Srwatson	ipxp = sotoipxpcb(so);
1499160549Srwatson	KASSERT(ipxp != NULL, ("spx_usr_close: ipxp == NULL"));
1500160549Srwatson
1501160549Srwatson	cb = ipxtospxpcb(ipxp);
1502160549Srwatson	KASSERT(cb != NULL, ("spx_usr_close: cb == NULL"));
1503160549Srwatson
1504160549Srwatson	IPX_LIST_LOCK();
1505160549Srwatson	IPX_LOCK(ipxp);
1506160549Srwatson	if (cb->s_state > TCPS_LISTEN)
1507160549Srwatson		spx_disconnect(cb);
1508160549Srwatson	else
1509160549Srwatson		spx_close(cb);
1510160549Srwatson	IPX_UNLOCK(ipxp);
1511160549Srwatson	IPX_LIST_UNLOCK();
1512160549Srwatson}
1513160549Srwatson
151424659Sjhay/*
1515157094Srwatson * Initiate connection to peer.  Enter SYN_SENT state, and mark socket as
1516157094Srwatson * connecting.  Start keep-alive timer, setup prototype header, send initial
1517157094Srwatson * system packet requesting connection.
151824659Sjhay */
151924659Sjhaystatic int
1520157067Srwatsonspx_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
152124659Sjhay{
152224659Sjhay	struct ipxpcb *ipxp;
152324659Sjhay	struct spxpcb *cb;
1524139932Srwatson	int error;
152511819Sjulian
152624659Sjhay	ipxp = sotoipxpcb(so);
1527157094Srwatson	KASSERT(ipxp != NULL, ("spx_connect: ipxp == NULL"));
1528157094Srwatson
152924659Sjhay	cb = ipxtospxpcb(ipxp);
1530157094Srwatson	KASSERT(cb != NULL, ("spx_connect: cb == NULL"));
153124659Sjhay
1532139932Srwatson	IPX_LIST_LOCK();
1533139932Srwatson	IPX_LOCK(ipxp);
1534157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1535157153Srwatson		error = EINVAL;
1536157153Srwatson		goto spx_connect_end;
1537157153Srwatson	}
153824659Sjhay	if (ipxp->ipxp_lport == 0) {
1539139579Srwatson		error = ipx_pcbbind(ipxp, NULL, td);
154024659Sjhay		if (error)
154124659Sjhay			goto spx_connect_end;
154224659Sjhay	}
154383366Sjulian	error = ipx_pcbconnect(ipxp, nam, td);
154424659Sjhay	if (error)
154524659Sjhay		goto spx_connect_end;
154624659Sjhay	soisconnecting(so);
154724659Sjhay	spxstat.spxs_connattempt++;
154824659Sjhay	cb->s_state = TCPS_SYN_SENT;
154924659Sjhay	cb->s_did = 0;
155024659Sjhay	spx_template(cb);
155124659Sjhay	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
155224659Sjhay	cb->s_force = 1 + SPXTV_KEEP;
1553179408Srwatson
155411819Sjulian	/*
1555157094Srwatson	 * Other party is required to respond to the port I send from, but he
1556157094Srwatson	 * is not required to answer from where I am sending to, so allow
1557157094Srwatson	 * wildcarding.  Original port I am sending to is still saved in
155824659Sjhay	 * cb->s_dport.
155911819Sjulian	 */
156024659Sjhay	ipxp->ipxp_fport = 0;
1561139579Srwatson	error = spx_output(cb, NULL);
156224659Sjhayspx_connect_end:
1563139932Srwatson	IPX_UNLOCK(ipxp);
1564139932Srwatson	IPX_LIST_UNLOCK();
156524659Sjhay	return (error);
156624659Sjhay}
156711819Sjulian
1568157370Srwatsonstatic void
1569157067Srwatsonspx_detach(struct socket *so)
157024659Sjhay{
157124659Sjhay	struct ipxpcb *ipxp;
157224659Sjhay	struct spxpcb *cb;
157311819Sjulian
1574160549Srwatson	/*
1575160549Srwatson	 * XXXRW: Should assert appropriately detached.
1576160549Srwatson	 */
157724659Sjhay	ipxp = sotoipxpcb(so);
1578157094Srwatson	KASSERT(ipxp != NULL, ("spx_detach: ipxp == NULL"));
1579157094Srwatson
158024659Sjhay	cb = ipxtospxpcb(ipxp);
1581157094Srwatson	KASSERT(cb != NULL, ("spx_detach: cb == NULL"));
158211819Sjulian
1583139932Srwatson	IPX_LIST_LOCK();
1584139932Srwatson	IPX_LOCK(ipxp);
1585157128Srwatson	spx_pcbdetach(ipxp);
1586157128Srwatson	ipx_pcbfree(ipxp);
1587139932Srwatson	IPX_LIST_UNLOCK();
158824659Sjhay}
158911819Sjulian
159024659Sjhay/*
1591157094Srwatson * We may decide later to implement connection closing handshaking at the spx
1592157094Srwatson * level optionally.  Here is the hook to do it:
159324659Sjhay */
159424659Sjhaystatic int
1595157067Srwatsonspx_usr_disconnect(struct socket *so)
159624659Sjhay{
159724659Sjhay	struct ipxpcb *ipxp;
159824659Sjhay	struct spxpcb *cb;
1599157153Srwatson	int error;
160011819Sjulian
160124659Sjhay	ipxp = sotoipxpcb(so);
1602157094Srwatson	KASSERT(ipxp != NULL, ("spx_usr_disconnect: ipxp == NULL"));
1603157094Srwatson
160424659Sjhay	cb = ipxtospxpcb(ipxp);
1605157094Srwatson	KASSERT(cb != NULL, ("spx_usr_disconnect: cb == NULL"));
160611819Sjulian
1607139932Srwatson	IPX_LIST_LOCK();
1608139932Srwatson	IPX_LOCK(ipxp);
1609157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1610157153Srwatson		error = EINVAL;
1611157153Srwatson		goto out;
1612157153Srwatson	}
161324659Sjhay	spx_disconnect(cb);
1614157153Srwatson	error = 0;
1615157153Srwatsonout:
1616157128Srwatson	IPX_UNLOCK(ipxp);
1617139932Srwatson	IPX_LIST_UNLOCK();
1618157153Srwatson	return (error);
161924659Sjhay}
162011819Sjulian
162124659Sjhaystatic int
1622157067Srwatsonspx_listen(struct socket *so, int backlog, struct thread *td)
162324659Sjhay{
162424659Sjhay	int error;
162524659Sjhay	struct ipxpcb *ipxp;
162624659Sjhay	struct spxpcb *cb;
162711819Sjulian
162824659Sjhay	error = 0;
162924659Sjhay	ipxp = sotoipxpcb(so);
1630157094Srwatson	KASSERT(ipxp != NULL, ("spx_listen: ipxp == NULL"));
1631157094Srwatson
163224659Sjhay	cb = ipxtospxpcb(ipxp);
1633157094Srwatson	KASSERT(cb != NULL, ("spx_listen: cb == NULL"));
163411819Sjulian
1635139932Srwatson	IPX_LIST_LOCK();
1636139932Srwatson	IPX_LOCK(ipxp);
1637157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1638157153Srwatson		error = EINVAL;
1639157153Srwatson		goto out;
1640157153Srwatson	}
1641142190Srwatson	SOCK_LOCK(so);
1642142190Srwatson	error = solisten_proto_check(so);
1643142190Srwatson	if (error == 0 && ipxp->ipxp_lport == 0)
1644139579Srwatson		error = ipx_pcbbind(ipxp, NULL, td);
1645142190Srwatson	if (error == 0) {
164624659Sjhay		cb->s_state = TCPS_LISTEN;
1647151888Srwatson		solisten_proto(so, backlog);
1648142190Srwatson	}
1649142190Srwatson	SOCK_UNLOCK(so);
1650157153Srwatsonout:
1651139932Srwatson	IPX_UNLOCK(ipxp);
1652139932Srwatson	IPX_LIST_UNLOCK();
165324659Sjhay	return (error);
165424659Sjhay}
165511819Sjulian
165624659Sjhay/*
1657157094Srwatson * After a receive, possibly send acknowledgment updating allocation.
165824659Sjhay */
165924659Sjhaystatic int
1660157067Srwatsonspx_rcvd(struct socket *so, int flags)
166124659Sjhay{
166224659Sjhay	struct ipxpcb *ipxp;
166324659Sjhay	struct spxpcb *cb;
1664157153Srwatson	int error;
166511819Sjulian
166624659Sjhay	ipxp = sotoipxpcb(so);
1667157094Srwatson	KASSERT(ipxp != NULL, ("spx_rcvd: ipxp == NULL"));
1668157094Srwatson
166924659Sjhay	cb = ipxtospxpcb(ipxp);
1670157094Srwatson	KASSERT(cb != NULL, ("spx_rcvd: cb == NULL"));
167111819Sjulian
1672139932Srwatson	IPX_LOCK(ipxp);
1673157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1674157153Srwatson		error = EINVAL;
1675157153Srwatson		goto out;
1676157153Srwatson	}
167724659Sjhay	cb->s_flags |= SF_RVD;
1678139579Srwatson	spx_output(cb, NULL);
167924659Sjhay	cb->s_flags &= ~SF_RVD;
1680157153Srwatson	error = 0;
1681157153Srwatsonout:
1682139932Srwatson	IPX_UNLOCK(ipxp);
1683157153Srwatson	return (error);
168424659Sjhay}
168511819Sjulian
168624659Sjhaystatic int
1687157067Srwatsonspx_rcvoob(struct socket *so, struct mbuf *m, int flags)
168824659Sjhay{
168924659Sjhay	struct ipxpcb *ipxp;
169024659Sjhay	struct spxpcb *cb;
1691157153Srwatson	int error;
169211819Sjulian
169324659Sjhay	ipxp = sotoipxpcb(so);
1694157094Srwatson	KASSERT(ipxp != NULL, ("spx_rcvoob: ipxp == NULL"));
1695157094Srwatson
169624659Sjhay	cb = ipxtospxpcb(ipxp);
1697157094Srwatson	KASSERT(cb != NULL, ("spx_rcvoob: cb == NULL"));
169811819Sjulian
1699157128Srwatson	IPX_LOCK(ipxp);
1700157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1701157153Srwatson		error = EINVAL;
1702157153Srwatson		goto out;
1703157153Srwatson	}
1704139591Srwatson	SOCKBUF_LOCK(&so->so_rcv);
170524659Sjhay	if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark ||
1706130480Srwatson	    (so->so_rcv.sb_state & SBS_RCVATMARK)) {
1707139591Srwatson		SOCKBUF_UNLOCK(&so->so_rcv);
170824659Sjhay		m->m_len = 1;
170924659Sjhay		*mtod(m, caddr_t) = cb->s_iobc;
1710157153Srwatson		error = 0;
1711157153Srwatson		goto out;
171211819Sjulian	}
1713139591Srwatson	SOCKBUF_UNLOCK(&so->so_rcv);
1714157153Srwatson	error = EINVAL;
1715157153Srwatsonout:
1716157128Srwatson	IPX_UNLOCK(ipxp);
1717157153Srwatson	return (error);
171824659Sjhay}
171924659Sjhay
172024659Sjhaystatic int
1721157067Srwatsonspx_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1722157067Srwatson    struct mbuf *controlp, struct thread *td)
172324659Sjhay{
172424659Sjhay	struct ipxpcb *ipxp;
172524659Sjhay	struct spxpcb *cb;
1726157153Srwatson	int error;
172724659Sjhay
172824659Sjhay	ipxp = sotoipxpcb(so);
1729157094Srwatson	KASSERT(ipxp != NULL, ("spx_send: ipxp == NULL"));
1730157094Srwatson
173124659Sjhay	cb = ipxtospxpcb(ipxp);
1732157094Srwatson	KASSERT(cb != NULL, ("spx_send: cb == NULL"));
173324659Sjhay
1734157094Srwatson	error = 0;
1735139932Srwatson	IPX_LOCK(ipxp);
1736157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1737157153Srwatson		error = ECONNRESET;
1738157153Srwatson		goto spx_send_end;
1739157153Srwatson	}
174024659Sjhay	if (flags & PRUS_OOB) {
174124659Sjhay		if (sbspace(&so->so_snd) < -512) {
174224659Sjhay			error = ENOBUFS;
174324659Sjhay			goto spx_send_end;
174424659Sjhay		}
174524659Sjhay		cb->s_oobflags |= SF_SOOB;
174624659Sjhay	}
174725652Sjhay	if (controlp != NULL) {
174824659Sjhay		u_short *p = mtod(controlp, u_short *);
174924659Sjhay		spx_newchecks[2]++;
175025652Sjhay		if ((p[0] == 5) && (p[1] == 1)) { /* XXXX, for testing */
175124659Sjhay			cb->s_shdr.spx_dt = *(u_char *)(&p[2]);
175224659Sjhay			spx_newchecks[3]++;
175324659Sjhay		}
175424659Sjhay		m_freem(controlp);
175524659Sjhay	}
175624659Sjhay	controlp = NULL;
175724659Sjhay	error = spx_output(cb, m);
175824659Sjhay	m = NULL;
175924659Sjhayspx_send_end:
1760139932Srwatson	IPX_UNLOCK(ipxp);
176111819Sjulian	if (controlp != NULL)
176211819Sjulian		m_freem(controlp);
176311819Sjulian	if (m != NULL)
176411819Sjulian		m_freem(m);
176511819Sjulian	return (error);
176611819Sjulian}
176711819Sjulian
176824659Sjhaystatic int
1769157067Srwatsonspx_shutdown(struct socket *so)
177024659Sjhay{
177124659Sjhay	struct ipxpcb *ipxp;
177224659Sjhay	struct spxpcb *cb;
1773157153Srwatson	int error;
177424659Sjhay
177524659Sjhay	ipxp = sotoipxpcb(so);
1776157094Srwatson	KASSERT(ipxp != NULL, ("spx_shutdown: ipxp == NULL"));
1777157094Srwatson
177824659Sjhay	cb = ipxtospxpcb(ipxp);
1779157094Srwatson	KASSERT(cb != NULL, ("spx_shutdown: cb == NULL"));
178024659Sjhay
178124659Sjhay	socantsendmore(so);
1782139932Srwatson	IPX_LIST_LOCK();
1783139932Srwatson	IPX_LOCK(ipxp);
1784157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1785157153Srwatson		error = EINVAL;
1786157153Srwatson		goto out;
1787157153Srwatson	}
1788139931Srwatson	spx_usrclosed(cb);
1789157153Srwatson	error = 0;
1790157153Srwatsonout:
1791157128Srwatson	IPX_UNLOCK(ipxp);
1792139932Srwatson	IPX_LIST_UNLOCK();
1793157153Srwatson	return (error);
179424659Sjhay}
179524659Sjhay
179624659Sjhaystatic int
1797157067Srwatsonspx_sp_attach(struct socket *so, int proto, struct thread *td)
179811819Sjulian{
1799157127Srwatson	struct ipxpcb *ipxp;
1800157127Srwatson	struct spxpcb *cb;
180124659Sjhay	int error;
180211819Sjulian
1803157127Srwatson	KASSERT(so->so_pcb == NULL, ("spx_sp_attach: so_pcb != NULL"));
1804157127Srwatson
180583366Sjulian	error = spx_attach(so, proto, td);
1806157127Srwatson	if (error)
1807157127Srwatson		return (error);
1808157127Srwatson
1809157127Srwatson	ipxp = sotoipxpcb(so);
1810157127Srwatson	KASSERT(ipxp != NULL, ("spx_sp_attach: ipxp == NULL"));
1811157127Srwatson
1812157127Srwatson	cb = ipxtospxpcb(ipxp);
1813157127Srwatson	KASSERT(cb != NULL, ("spx_sp_attach: cb == NULL"));
1814157127Srwatson
1815157127Srwatson	IPX_LOCK(ipxp);
1816157127Srwatson	cb->s_flags |= (SF_HI | SF_HO | SF_PI);
1817157127Srwatson	IPX_UNLOCK(ipxp);
1818157127Srwatson	return (0);
181911819Sjulian}
182011819Sjulian
182111819Sjulian/*
1822157094Srwatson * Create template to be used to send spx packets on a connection.  Called
1823157094Srwatson * after host entry created, fills in a skeletal spx header (choosing
1824157094Srwatson * connection id), minimizing the amount of work necessary when the
1825157094Srwatson * connection is used.
182611819Sjulian */
182725652Sjhaystatic void
1828157067Srwatsonspx_template(struct spxpcb *cb)
182911819Sjulian{
1830157067Srwatson	struct ipxpcb *ipxp = cb->s_ipxpcb;
1831157067Srwatson	struct ipx *ipx = cb->s_ipx;
1832157067Srwatson	struct sockbuf *sb = &(ipxp->ipxp_socket->so_snd);
183311819Sjulian
1834139932Srwatson	IPX_LOCK_ASSERT(ipxp);
1835139932Srwatson
183611819Sjulian	ipx->ipx_pt = IPXPROTO_SPX;
183711819Sjulian	ipx->ipx_sna = ipxp->ipxp_laddr;
183811819Sjulian	ipx->ipx_dna = ipxp->ipxp_faddr;
1839157069Srwatson	SPX_LOCK();
184011819Sjulian	cb->s_sid = htons(spx_iss);
184111819Sjulian	spx_iss += SPX_ISSINCR/2;
1842157069Srwatson	SPX_UNLOCK();
184311819Sjulian	cb->s_alo = 1;
184411819Sjulian	cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu;
1845179408Srwatson
1846179408Srwatson	/*
1847179408Srwatson	 * Try to expand fast to full complement of large packets.
1848179408Srwatson	 */
1849157094Srwatson	cb->s_ssthresh = cb->s_cwnd;
185011819Sjulian	cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spx));
1851179408Srwatson
1852179408Srwatson	/*
1853179408Srwatson	 * But allow for lots of little packets as well.
1854179408Srwatson	 */
185511819Sjulian	cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd);
185611819Sjulian}
185711819Sjulian
185811819Sjulian/*
1859157128Srwatson * Close a SPIP control block.  Wake up any sleepers.  We used to free any
1860157128Srwatson * queued packets and cb->s_ipx here, but now we defer that until the pcb is
1861157128Srwatson * discarded.
186211819Sjulian */
1863139931Srwatsonvoid
1864157067Srwatsonspx_close(struct spxpcb *cb)
186511819Sjulian{
186611819Sjulian	struct ipxpcb *ipxp = cb->s_ipxpcb;
186711819Sjulian	struct socket *so = ipxp->ipxp_socket;
186811819Sjulian
1869157094Srwatson	KASSERT(ipxp != NULL, ("spx_close: ipxp == NULL"));
1870139932Srwatson	IPX_LIST_LOCK_ASSERT();
1871139932Srwatson	IPX_LOCK_ASSERT(ipxp);
1872139932Srwatson
1873157128Srwatson	ipxp->ipxp_flags |= IPXP_DROPPED;
187411819Sjulian	soisdisconnected(so);
187511819Sjulian	spxstat.spxs_closed++;
187611819Sjulian}
187725652Sjhay
187811819Sjulian/*
1879157094Srwatson * Someday we may do level 3 handshaking to close a connection or send a
1880157094Srwatson * xerox style error.  For now, just close.  cb will always be invalid after
1881157094Srwatson * this call.
188211819Sjulian */
1883139931Srwatsonstatic void
1884157067Srwatsonspx_usrclosed(struct spxpcb *cb)
188511819Sjulian{
1886139931Srwatson
1887139932Srwatson	IPX_LIST_LOCK_ASSERT();
1888139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1889139932Srwatson
1890139931Srwatson	spx_close(cb);
189111819Sjulian}
189225652Sjhay
1893139931Srwatson/*
1894139931Srwatson * cb will always be invalid after this call.
1895139931Srwatson */
1896139931Srwatsonstatic void
1897157067Srwatsonspx_disconnect(struct spxpcb *cb)
189811819Sjulian{
1899139931Srwatson
1900139932Srwatson	IPX_LIST_LOCK_ASSERT();
1901139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1902139932Srwatson
1903139931Srwatson	spx_close(cb);
190411819Sjulian}
190525652Sjhay
190611819Sjulian/*
1907157094Srwatson * Drop connection, reporting the specified error.  cb will always be invalid
1908157094Srwatson * after this call.
190911819Sjulian */
1910139931Srwatsonstatic void
1911157067Srwatsonspx_drop(struct spxpcb *cb, int errno)
191211819Sjulian{
191311819Sjulian	struct socket *so = cb->s_ipxpcb->ipxp_socket;
191411819Sjulian
1915139932Srwatson	IPX_LIST_LOCK_ASSERT();
1916139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1917139932Srwatson
191811819Sjulian	/*
1919157094Srwatson	 * Someday, in the xerox world we will generate error protocol
1920157094Srwatson	 * packets announcing that the socket has gone away.
192111819Sjulian	 */
192211819Sjulian	if (TCPS_HAVERCVDSYN(cb->s_state)) {
192311819Sjulian		spxstat.spxs_drops++;
192411819Sjulian		cb->s_state = TCPS_CLOSED;
192525652Sjhay		/*tcp_output(cb);*/
192611819Sjulian	} else
192711819Sjulian		spxstat.spxs_conndrops++;
192811819Sjulian	so->so_error = errno;
1929139931Srwatson	spx_close(cb);
193011819Sjulian}
193111819Sjulian
193211819Sjulian/*
1933157094Srwatson * Fast timeout routine for processing delayed acks.
193411819Sjulian */
193511819Sjulianvoid
1936157067Srwatsonspx_fasttimo(void)
193711819Sjulian{
1938139932Srwatson	struct ipxpcb *ipxp;
1939139932Srwatson	struct spxpcb *cb;
194011819Sjulian
1941139932Srwatson	IPX_LIST_LOCK();
1942139444Srwatson	LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) {
1943139932Srwatson		IPX_LOCK(ipxp);
1944157145Srwatson		if (!(ipxp->ipxp_flags & IPXP_SPX) ||
1945157145Srwatson		    (ipxp->ipxp_flags & IPXP_DROPPED)) {
1946157145Srwatson			IPX_UNLOCK(ipxp);
1947157145Srwatson			continue;
194811819Sjulian		}
1949157145Srwatson		cb = ipxtospxpcb(ipxp);
1950157145Srwatson		if (cb->s_flags & SF_DELACK) {
1951157145Srwatson			cb->s_flags &= ~SF_DELACK;
1952157145Srwatson			cb->s_flags |= SF_ACKNOW;
1953157145Srwatson			spxstat.spxs_delack++;
1954157145Srwatson			spx_output(cb, NULL);
1955157145Srwatson		}
1956139932Srwatson		IPX_UNLOCK(ipxp);
1957139444Srwatson	}
1958139932Srwatson	IPX_LIST_UNLOCK();
195911819Sjulian}
196011819Sjulian
196111819Sjulian/*
1962157094Srwatson * spx protocol timeout routine called every 500 ms.  Updates the timers in
1963157094Srwatson * all active pcb's and causes finite state machine actions if timers expire.
196411819Sjulian */
196511819Sjulianvoid
1966157067Srwatsonspx_slowtimo(void)
196711819Sjulian{
1968157128Srwatson	struct ipxpcb *ipxp;
1969139932Srwatson	struct spxpcb *cb;
1970139932Srwatson	int i;
197111819Sjulian
197211819Sjulian	/*
1973157128Srwatson	 * Search through tcb's and update active timers.  Once, timers could
1974157128Srwatson	 * free ipxp's, but now we do that only when detaching a socket.
197511819Sjulian	 */
1976139932Srwatson	IPX_LIST_LOCK();
1977157128Srwatson	LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) {
1978157128Srwatson		IPX_LOCK(ipxp);
1979157145Srwatson		if (!(ipxp->ipxp_flags & IPXP_SPX) ||
1980157145Srwatson		    (ipxp->ipxp_flags & IPXP_DROPPED)) {
1981157128Srwatson			IPX_UNLOCK(ipxp);
1982139444Srwatson			continue;
1983157128Srwatson		}
1984157128Srwatson
1985157128Srwatson		cb = (struct spxpcb *)ipxp->ipxp_pcb;
1986157128Srwatson		KASSERT(cb != NULL, ("spx_slowtimo: cb == NULL"));
198711819Sjulian		for (i = 0; i < SPXT_NTIMERS; i++) {
198811819Sjulian			if (cb->s_timer[i] && --cb->s_timer[i] == 0) {
1989157128Srwatson				spx_timers(cb, i);
1990157128Srwatson				if (ipxp->ipxp_flags & IPXP_DROPPED)
1991139581Srwatson					break;
199211819Sjulian			}
199311819Sjulian		}
1994157128Srwatson		if (!(ipxp->ipxp_flags & IPXP_DROPPED)) {
1995139581Srwatson			cb->s_idle++;
1996139581Srwatson			if (cb->s_rtt)
1997139581Srwatson				cb->s_rtt++;
1998139581Srwatson		}
1999157128Srwatson		IPX_UNLOCK(ipxp);
200011819Sjulian	}
2001157069Srwatson	IPX_LIST_UNLOCK();
2002157069Srwatson	SPX_LOCK();
200311819Sjulian	spx_iss += SPX_ISSINCR/PR_SLOWHZ;		/* increment iss */
2004157069Srwatson	SPX_UNLOCK();
200511819Sjulian}
200625652Sjhay
200711819Sjulian/*
200811819Sjulian * SPX timer processing.
200911819Sjulian */
2010157128Srwatsonstatic void
2011157067Srwatsonspx_timers(struct spxpcb *cb, int timer)
201211819Sjulian{
201311819Sjulian	long rexmt;
201411819Sjulian	int win;
201511819Sjulian
2016139932Srwatson	IPX_LIST_LOCK_ASSERT();
2017139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
2018139932Srwatson
201911819Sjulian	cb->s_force = 1 + timer;
202011819Sjulian	switch (timer) {
202111819Sjulian	case SPXT_2MSL:
2022157124Srwatson		/*
2023157124Srwatson		 * 2 MSL timeout in shutdown went off.  TCP deletes
2024157124Srwatson		 * connection control block.
2025157124Srwatson		 */
202611819Sjulian		printf("spx: SPXT_2MSL went off for no reason\n");
202711819Sjulian		cb->s_timer[timer] = 0;
202811819Sjulian		break;
202911819Sjulian
203011819Sjulian	case SPXT_REXMT:
2031157124Srwatson		/*
2032157124Srwatson		 * Retransmission timer went off.  Message has not been acked
2033157124Srwatson		 * within retransmit interval.  Back off to a longer
2034157124Srwatson		 * retransmit interval and retransmit one packet.
2035157124Srwatson		 */
203611819Sjulian		if (++cb->s_rxtshift > SPX_MAXRXTSHIFT) {
203711819Sjulian			cb->s_rxtshift = SPX_MAXRXTSHIFT;
203811819Sjulian			spxstat.spxs_timeoutdrop++;
2039139931Srwatson			spx_drop(cb, ETIMEDOUT);
204011819Sjulian			break;
204111819Sjulian		}
204211819Sjulian		spxstat.spxs_rexmttimeo++;
204311819Sjulian		rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
204411819Sjulian		rexmt *= spx_backoff[cb->s_rxtshift];
204511819Sjulian		SPXT_RANGESET(cb->s_rxtcur, rexmt, SPXTV_MIN, SPXTV_REXMTMAX);
204611819Sjulian		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
2047157094Srwatson
204811819Sjulian		/*
2049157094Srwatson		 * If we have backed off fairly far, our srtt estimate is
2050157094Srwatson		 * probably bogus.  Clobber it so we'll take the next rtt
2051157094Srwatson		 * measurement as our srtt; move the current srtt into rttvar
2052157094Srwatson		 * to keep the current retransmit times until then.
205311819Sjulian		 */
205411819Sjulian		if (cb->s_rxtshift > SPX_MAXRXTSHIFT / 4 ) {
205511819Sjulian			cb->s_rttvar += (cb->s_srtt >> 2);
205611819Sjulian			cb->s_srtt = 0;
205711819Sjulian		}
205811819Sjulian		cb->s_snxt = cb->s_rack;
2059157094Srwatson
206011819Sjulian		/*
206111819Sjulian		 * If timing a packet, stop the timer.
206211819Sjulian		 */
206311819Sjulian		cb->s_rtt = 0;
2064157094Srwatson
206511819Sjulian		/*
206611819Sjulian		 * See very long discussion in tcp_timer.c about congestion
2067157094Srwatson		 * window and sstrhesh.
206811819Sjulian		 */
206911819Sjulian		win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2;
207011819Sjulian		if (win < 2)
207111819Sjulian			win = 2;
207211819Sjulian		cb->s_cwnd = CUNIT;
207311819Sjulian		cb->s_ssthresh = win * CUNIT;
2074139579Srwatson		spx_output(cb, NULL);
207511819Sjulian		break;
207611819Sjulian
207711819Sjulian	case SPXT_PERSIST:
2078157094Srwatson		/*
2079157094Srwatson		 * Persistance timer into zero window.  Force a probe to be
2080157094Srwatson		 * sent.
2081157094Srwatson		 */
208211819Sjulian		spxstat.spxs_persisttimeo++;
208311819Sjulian		spx_setpersist(cb);
2084139579Srwatson		spx_output(cb, NULL);
208511819Sjulian		break;
208611819Sjulian
208711819Sjulian	case SPXT_KEEP:
2088157094Srwatson		/*
2089157094Srwatson		 * Keep-alive timer went off; send something or drop
2090157094Srwatson		 * connection if idle for too long.
2091157094Srwatson		 */
209211819Sjulian		spxstat.spxs_keeptimeo++;
209311819Sjulian		if (cb->s_state < TCPS_ESTABLISHED)
209411819Sjulian			goto dropit;
209511819Sjulian		if (cb->s_ipxpcb->ipxp_socket->so_options & SO_KEEPALIVE) {
209611819Sjulian		    	if (cb->s_idle >= SPXTV_MAXIDLE)
209711819Sjulian				goto dropit;
209811819Sjulian			spxstat.spxs_keepprobe++;
2099139579Srwatson			spx_output(cb, NULL);
210097658Stanimura		} else
210111819Sjulian			cb->s_idle = 0;
210211819Sjulian		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
210311819Sjulian		break;
2104157094Srwatson
210511819Sjulian	dropit:
210611819Sjulian		spxstat.spxs_keepdrops++;
2107139931Srwatson		spx_drop(cb, ETIMEDOUT);
210811819Sjulian		break;
2109157124Srwatson
2110157124Srwatson	default:
2111157124Srwatson		panic("spx_timers: unknown timer %d", timer);
211211819Sjulian	}
211311819Sjulian}
2114