1139823Simp/*-
2157067Srwatson * Copyright (c) 1984, 1985, 1986, 1987, 1993
3157067Srwatson *	The Regents of the University of California.
4192753Srwatson * Copyright (c) 2004-2009 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$");
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
92194545Srwatson#include <security/mac/mac_framework.h>
93194545Srwatson
9411819Sjulian/*
9511819Sjulian * SPX protocol implementation.
9611819Sjulian */
97157069Srwatsonstatic struct	mtx spx_mtx;			/* Protects only spx_iss. */
9833181Seivindstatic u_short 	spx_iss;
99192746Srwatsonu_short		spx_newchecks[50];
10033181Seivindstatic int	spx_hardnosed;
10133181Seivindstatic int	traceallspxs = 0;
102192746Srwatsonstruct	spx_istat spx_istat;
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
108132045Srwatsonstatic const int spx_backoff[SPX_MAXRXTSHIFT+1] =
10925652Sjhay    { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
11011819Sjulian
111139931Srwatsonstatic	void spx_close(struct spxpcb *cb);
112139931Srwatsonstatic	void spx_disconnect(struct spxpcb *cb);
113139931Srwatsonstatic	void spx_drop(struct spxpcb *cb, int errno);
11425652Sjhaystatic	void spx_setpersist(struct spxpcb *cb);
11525652Sjhaystatic	void spx_template(struct spxpcb *cb);
116157128Srwatsonstatic	void spx_timers(struct spxpcb *cb, int timer);
117139931Srwatsonstatic	void spx_usrclosed(struct spxpcb *cb);
11825652Sjhay
119157366Srwatsonstatic	void spx_usr_abort(struct socket *so);
12028270Swollmanstatic	int spx_accept(struct socket *so, struct sockaddr **nam);
12183366Sjulianstatic	int spx_attach(struct socket *so, int proto, struct thread *td);
12283366Sjulianstatic	int spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td);
123160549Srwatsonstatic	void spx_usr_close(struct socket *so);
12428270Swollmanstatic	int spx_connect(struct socket *so, struct sockaddr *nam,
12583366Sjulian			struct thread *td);
126157370Srwatsonstatic	void spx_detach(struct socket *so);
127157128Srwatsonstatic	void spx_pcbdetach(struct ipxpcb *ipxp);
12824659Sjhaystatic	int spx_usr_disconnect(struct socket *so);
129151888Srwatsonstatic	int spx_listen(struct socket *so, int backlog, struct thread *td);
13024659Sjhaystatic	int spx_rcvd(struct socket *so, int flags);
13124659Sjhaystatic	int spx_rcvoob(struct socket *so, struct mbuf *m, int flags);
13224659Sjhaystatic	int spx_send(struct socket *so, int flags, struct mbuf *m,
133139584Srwatson		     struct sockaddr *addr, struct mbuf *control,
13483366Sjulian		     struct thread *td);
13524659Sjhaystatic	int spx_shutdown(struct socket *so);
13683366Sjulianstatic	int spx_sp_attach(struct socket *so, int proto, struct thread *td);
13724659Sjhay
13824659Sjhaystruct	pr_usrreqs spx_usrreqs = {
139137386Sphk	.pru_abort =		spx_usr_abort,
140137386Sphk	.pru_accept =		spx_accept,
141137386Sphk	.pru_attach =		spx_attach,
142137386Sphk	.pru_bind =		spx_bind,
143137386Sphk	.pru_connect =		spx_connect,
144137386Sphk	.pru_control =		ipx_control,
145137386Sphk	.pru_detach =		spx_detach,
146137386Sphk	.pru_disconnect =	spx_usr_disconnect,
147137386Sphk	.pru_listen =		spx_listen,
148137386Sphk	.pru_peeraddr =		ipx_peeraddr,
149137386Sphk	.pru_rcvd =		spx_rcvd,
150137386Sphk	.pru_rcvoob =		spx_rcvoob,
151137386Sphk	.pru_send =		spx_send,
152137386Sphk	.pru_shutdown =		spx_shutdown,
153137386Sphk	.pru_sockaddr =		ipx_sockaddr,
154160549Srwatson	.pru_close =		spx_usr_close,
15524659Sjhay};
15624659Sjhay
15724659Sjhaystruct	pr_usrreqs spx_usrreq_sps = {
158137386Sphk	.pru_abort =		spx_usr_abort,
159137386Sphk	.pru_accept =		spx_accept,
160137386Sphk	.pru_attach =		spx_sp_attach,
161137386Sphk	.pru_bind =		spx_bind,
162137386Sphk	.pru_connect =		spx_connect,
163137386Sphk	.pru_control =		ipx_control,
164137386Sphk	.pru_detach =		spx_detach,
165137386Sphk	.pru_disconnect =	spx_usr_disconnect,
166137386Sphk	.pru_listen =		spx_listen,
167137386Sphk	.pru_peeraddr =		ipx_peeraddr,
168137386Sphk	.pru_rcvd =		spx_rcvd,
169137386Sphk	.pru_rcvoob =		spx_rcvoob,
170137386Sphk	.pru_send =		spx_send,
171137386Sphk	.pru_shutdown =		spx_shutdown,
172137386Sphk	.pru_sockaddr =		ipx_sockaddr,
173160549Srwatson	.pru_close =		spx_usr_close,
17424659Sjhay};
17524659Sjhay
17611819Sjulianvoid
177157067Srwatsonspx_init(void)
17811819Sjulian{
17911819Sjulian
180157069Srwatson	SPX_LOCK_INIT();
18111819Sjulian	spx_iss = 1; /* WRONG !! should fish it out of TODR */
18211819Sjulian}
18311819Sjulian
18411819Sjulianvoid
185157067Srwatsonspx_input(struct mbuf *m, struct ipxpcb *ipxp)
18611819Sjulian{
187157067Srwatson	struct spxpcb *cb;
188157067Srwatson	struct spx *si = mtod(m, struct spx *);
189157067Srwatson	struct socket *so;
190157051Srwatson	struct spx spx_savesi;
19111819Sjulian	int dropsocket = 0;
19211819Sjulian	short ostate = 0;
19311819Sjulian
19411819Sjulian	spxstat.spxs_rcvtotal++;
195157094Srwatson	KASSERT(ipxp != NULL, ("spx_input: ipxpcb == NULL"));
19611819Sjulian
197139932Srwatson	/*
198139932Srwatson	 * spx_input() assumes that the caller will hold both the pcb list
199139932Srwatson	 * lock and also the ipxp lock.  spx_input() will release both before
200139932Srwatson	 * returning, and may in fact trade in the ipxp lock for another pcb
201139932Srwatson	 * lock following sonewconn().
202139932Srwatson	 */
203139932Srwatson	IPX_LIST_LOCK_ASSERT();
204139932Srwatson	IPX_LOCK_ASSERT(ipxp);
205139932Srwatson
20611819Sjulian	cb = ipxtospxpcb(ipxp);
207157128Srwatson	KASSERT(cb != NULL, ("spx_input: cb == NULL"));
20811819Sjulian
209157128Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED)
210157128Srwatson		goto drop;
211157128Srwatson
21211819Sjulian	if (m->m_len < sizeof(*si)) {
21325652Sjhay		if ((m = m_pullup(m, sizeof(*si))) == NULL) {
214139932Srwatson			IPX_UNLOCK(ipxp);
215139932Srwatson			IPX_LIST_UNLOCK();
21611819Sjulian			spxstat.spxs_rcvshort++;
21711819Sjulian			return;
21811819Sjulian		}
21911819Sjulian		si = mtod(m, struct spx *);
22011819Sjulian	}
22111819Sjulian	si->si_seq = ntohs(si->si_seq);
22211819Sjulian	si->si_ack = ntohs(si->si_ack);
22311819Sjulian	si->si_alo = ntohs(si->si_alo);
22411819Sjulian
22511819Sjulian	so = ipxp->ipxp_socket;
226157094Srwatson	KASSERT(so != NULL, ("spx_input: so == NULL"));
22711819Sjulian
228194561Srwatson#ifdef MAC
229194561Srwatson	if (mac_socket_check_deliver(so, m) != 0)
230194561Srwatson		goto drop;
231194561Srwatson#endif
232194561Srwatson
23311819Sjulian	if (so->so_options & SO_DEBUG || traceallspxs) {
23411819Sjulian		ostate = cb->s_state;
23511819Sjulian		spx_savesi = *si;
23611819Sjulian	}
23711819Sjulian	if (so->so_options & SO_ACCEPTCONN) {
23811819Sjulian		struct spxpcb *ocb = cb;
23911819Sjulian
24011819Sjulian		so = sonewconn(so, 0);
241157094Srwatson		if (so == NULL)
24211819Sjulian			goto drop;
243157094Srwatson
24411819Sjulian		/*
24511819Sjulian		 * This is ugly, but ....
24611819Sjulian		 *
247157094Srwatson		 * Mark socket as temporary until we're committed to keeping
248157094Srwatson		 * it.  The code at ``drop'' and ``dropwithreset'' check the
249157094Srwatson		 * flag dropsocket to see if the temporary socket created
250157094Srwatson		 * here should be discarded.  We mark the socket as
251157094Srwatson		 * discardable until we're committed to it below in
252157094Srwatson		 * TCPS_LISTEN.
253157128Srwatson		 *
254157128Srwatson		 * XXXRW: In the new world order of real kernel parallelism,
255157128Srwatson		 * temporarily allocating the socket when we're "not sure"
256157128Srwatson		 * seems like a bad idea, as we might race to remove it if
257157128Srwatson		 * the listen socket is closed...?
258157128Srwatson		 *
259157128Srwatson		 * We drop the lock of the listen socket ipxp, and acquire
260157128Srwatson		 * the lock of the new socket ippx.
26111819Sjulian		 */
26211819Sjulian		dropsocket++;
263139932Srwatson		IPX_UNLOCK(ipxp);
26411819Sjulian		ipxp = (struct ipxpcb *)so->so_pcb;
265139932Srwatson		IPX_LOCK(ipxp);
26611819Sjulian		ipxp->ipxp_laddr = si->si_dna;
26711819Sjulian		cb = ipxtospxpcb(ipxp);
26811819Sjulian		cb->s_mtu = ocb->s_mtu;		/* preserve sockopts */
26911819Sjulian		cb->s_flags = ocb->s_flags;	/* preserve sockopts */
27011819Sjulian		cb->s_flags2 = ocb->s_flags2;	/* preserve sockopts */
27111819Sjulian		cb->s_state = TCPS_LISTEN;
27297658Stanimura	}
273157094Srwatson	IPX_LOCK_ASSERT(ipxp);
27411819Sjulian
27511819Sjulian	/*
276157094Srwatson	 * Packet received on connection.  Reset idle time and keep-alive
277157094Srwatson	 * timer.
27811819Sjulian	 */
27911819Sjulian	cb->s_idle = 0;
28011819Sjulian	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
28111819Sjulian
28211819Sjulian	switch (cb->s_state) {
28311819Sjulian	case TCPS_LISTEN:{
28428270Swollman		struct sockaddr_ipx *sipx, ssipx;
28511819Sjulian		struct ipx_addr laddr;
28611819Sjulian
28711819Sjulian		/*
288157094Srwatson		 * If somebody here was carying on a conversation and went
289157094Srwatson		 * away, and his pen pal thinks he can still talk, we get the
290157094Srwatson		 * misdirected packet.
29111819Sjulian		 */
29211819Sjulian		if (spx_hardnosed && (si->si_did != 0 || si->si_seq != 0)) {
29311819Sjulian			spx_istat.gonawy++;
29411819Sjulian			goto dropwithreset;
29511819Sjulian		}
29628270Swollman		sipx = &ssipx;
29728270Swollman		bzero(sipx, sizeof *sipx);
29811819Sjulian		sipx->sipx_len = sizeof(*sipx);
29911819Sjulian		sipx->sipx_family = AF_IPX;
30011819Sjulian		sipx->sipx_addr = si->si_sna;
30111819Sjulian		laddr = ipxp->ipxp_laddr;
30211819Sjulian		if (ipx_nullhost(laddr))
30311819Sjulian			ipxp->ipxp_laddr = si->si_dna;
30490361Sjulian		if (ipx_pcbconnect(ipxp, (struct sockaddr *)sipx, &thread0)) {
30511819Sjulian			ipxp->ipxp_laddr = laddr;
30611819Sjulian			spx_istat.noconn++;
30711819Sjulian			goto drop;
30811819Sjulian		}
30911819Sjulian		spx_template(cb);
31011819Sjulian		dropsocket = 0;		/* committed to socket */
31111819Sjulian		cb->s_did = si->si_sid;
31211819Sjulian		cb->s_rack = si->si_ack;
31311819Sjulian		cb->s_ralo = si->si_alo;
31411819Sjulian#define THREEWAYSHAKE
31511819Sjulian#ifdef THREEWAYSHAKE
31611819Sjulian		cb->s_state = TCPS_SYN_RECEIVED;
31711819Sjulian		cb->s_force = 1 + SPXT_KEEP;
31811819Sjulian		spxstat.spxs_accepts++;
31911819Sjulian		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
32011819Sjulian		}
32111819Sjulian		break;
322157094Srwatson
32311819Sjulian	 case TCPS_SYN_RECEIVED: {
324157094Srwatson		/*
325157094Srwatson		 * This state means that we have heard a response to our
326157094Srwatson		 * acceptance of their connection.  It is probably logically
327157094Srwatson		 * unnecessary in this implementation.
328157094Srwatson		 */
32925652Sjhay		if (si->si_did != cb->s_sid) {
33011819Sjulian			spx_istat.wrncon++;
33111819Sjulian			goto drop;
33211819Sjulian		}
33311819Sjulian#endif
33411819Sjulian		ipxp->ipxp_fport =  si->si_sport;
33511819Sjulian		cb->s_timer[SPXT_REXMT] = 0;
33611819Sjulian		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
33711819Sjulian		soisconnected(so);
33811819Sjulian		cb->s_state = TCPS_ESTABLISHED;
33911819Sjulian		spxstat.spxs_accepts++;
34011819Sjulian		}
34111819Sjulian		break;
34211819Sjulian
34311819Sjulian	case TCPS_SYN_SENT:
344157094Srwatson		/*
345157094Srwatson		 * This state means that we have gotten a response to our
346157094Srwatson		 * attempt to establish a connection.  We fill in the data
347157094Srwatson		 * from the other side, telling us which port to respond to,
348157094Srwatson		 * instead of the well-known one we might have sent to in the
349157094Srwatson		 * first place.  We also require that this is a response to
350157094Srwatson		 * our connection id.
351157094Srwatson		 */
35225652Sjhay		if (si->si_did != cb->s_sid) {
35311819Sjulian			spx_istat.notme++;
35411819Sjulian			goto drop;
35511819Sjulian		}
35611819Sjulian		spxstat.spxs_connects++;
35711819Sjulian		cb->s_did = si->si_sid;
35811819Sjulian		cb->s_rack = si->si_ack;
35911819Sjulian		cb->s_ralo = si->si_alo;
36011819Sjulian		cb->s_dport = ipxp->ipxp_fport =  si->si_sport;
36111819Sjulian		cb->s_timer[SPXT_REXMT] = 0;
36211819Sjulian		cb->s_flags |= SF_ACKNOW;
36311819Sjulian		soisconnected(so);
36411819Sjulian		cb->s_state = TCPS_ESTABLISHED;
365179408Srwatson
366157094Srwatson		/*
367157094Srwatson		 * Use roundtrip time of connection request for initial rtt.
368157094Srwatson		 */
36911819Sjulian		if (cb->s_rtt) {
37011819Sjulian			cb->s_srtt = cb->s_rtt << 3;
37111819Sjulian			cb->s_rttvar = cb->s_rtt << 1;
37211819Sjulian			SPXT_RANGESET(cb->s_rxtcur,
37311819Sjulian			    ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
37411819Sjulian			    SPXTV_MIN, SPXTV_REXMTMAX);
37511819Sjulian			    cb->s_rtt = 0;
37611819Sjulian		}
37711819Sjulian	}
378157094Srwatson
37997658Stanimura	if (so->so_options & SO_DEBUG || traceallspxs)
38011819Sjulian		spx_trace(SA_INPUT, (u_char)ostate, cb, &spx_savesi, 0);
38111819Sjulian
38225652Sjhay	m->m_len -= sizeof(struct ipx);
38325652Sjhay	m->m_pkthdr.len -= sizeof(struct ipx);
38425652Sjhay	m->m_data += sizeof(struct ipx);
38511819Sjulian
386194547Srwatson	if (spx_reass(cb, m, si))
38725652Sjhay		m_freem(m);
38811819Sjulian	if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT)))
389139579Srwatson		spx_output(cb, NULL);
39011819Sjulian	cb->s_flags &= ~(SF_WIN|SF_RXT);
391139932Srwatson	IPX_UNLOCK(ipxp);
392139932Srwatson	IPX_LIST_UNLOCK();
39311819Sjulian	return;
39411819Sjulian
39511819Sjuliandropwithreset:
396157094Srwatson	IPX_LOCK_ASSERT(ipxp);
397157164Srwatson	if (cb == NULL || (cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG ||
398157128Srwatson	    traceallspxs))
399157128Srwatson		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
400139932Srwatson	IPX_UNLOCK(ipxp);
401130822Srwatson	if (dropsocket) {
402130822Srwatson		struct socket *head;
403130822Srwatson		ACCEPT_LOCK();
404130822Srwatson		KASSERT((so->so_qstate & SQ_INCOMP) != 0,
405130822Srwatson		    ("spx_input: nascent socket not SQ_INCOMP on soabort()"));
406130822Srwatson		head = so->so_head;
407130822Srwatson		TAILQ_REMOVE(&head->so_incomp, so, so_list);
408130822Srwatson		head->so_incqlen--;
409130822Srwatson		so->so_qstate &= ~SQ_INCOMP;
410130822Srwatson		so->so_head = NULL;
411130822Srwatson		ACCEPT_UNLOCK();
41225652Sjhay		soabort(so);
413130822Srwatson	}
414139932Srwatson	IPX_LIST_UNLOCK();
415179332Srwatson	m_freem(m);
41611819Sjulian	return;
41711819Sjulian
41811819Sjuliandrop:
419157094Srwatson	IPX_LOCK_ASSERT(ipxp);
420157128Srwatson	if (cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || traceallspxs)
42111819Sjulian		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
422139932Srwatson	IPX_UNLOCK(ipxp);
423139932Srwatson	IPX_LIST_UNLOCK();
42411819Sjulian	m_freem(m);
42511819Sjulian}
42611819Sjulian
427192754Srwatsonvoid
428192754Srwatsonspx_ctlinput(int cmd, struct sockaddr *arg_as_sa, void *dummy)
429192754Srwatson{
430192754Srwatson
431192754Srwatson	/* Currently, nothing. */
432192754Srwatson}
433192754Srwatson
434192746Srwatsonint
435157067Srwatsonspx_output(struct spxpcb *cb, struct mbuf *m0)
43611819Sjulian{
43711819Sjulian	struct socket *so = cb->s_ipxpcb->ipxp_socket;
438192756Srwatson	struct mbuf *m = NULL;
439157067Srwatson	struct spx *si = NULL;
440157067Srwatson	struct sockbuf *sb = &so->so_snd;
44111819Sjulian	int len = 0, win, rcv_win;
44211819Sjulian	short span, off, recordp = 0;
44311819Sjulian	u_short alo;
44411819Sjulian	int error = 0, sendalot;
44511819Sjulian#ifdef notdef
44611819Sjulian	int idle;
44711819Sjulian#endif
44811819Sjulian	struct mbuf *mprev;
44911819Sjulian
450139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
451139932Srwatson
45225652Sjhay	if (m0 != NULL) {
45311819Sjulian		int mtu = cb->s_mtu;
45411819Sjulian		int datalen;
455157094Srwatson
45611819Sjulian		/*
45711819Sjulian		 * Make sure that packet isn't too big.
45811819Sjulian		 */
45925652Sjhay		for (m = m0; m != NULL; m = m->m_next) {
46011819Sjulian			mprev = m;
46111819Sjulian			len += m->m_len;
46211819Sjulian			if (m->m_flags & M_EOR)
46311819Sjulian				recordp = 1;
46411819Sjulian		}
46511819Sjulian		datalen = (cb->s_flags & SF_HO) ?
46625652Sjhay				len - sizeof(struct spxhdr) : len;
46711819Sjulian		if (datalen > mtu) {
46811819Sjulian			if (cb->s_flags & SF_PI) {
46911819Sjulian				m_freem(m0);
47011819Sjulian				return (EMSGSIZE);
47111819Sjulian			} else {
47211819Sjulian				int oldEM = cb->s_cc & SPX_EM;
47311819Sjulian
47411819Sjulian				cb->s_cc &= ~SPX_EM;
47511819Sjulian				while (len > mtu) {
476243882Sglebius					m = m_copym(m0, 0, mtu, M_NOWAIT);
477157167Srwatson					if (m == NULL) {
478157167Srwatson					    cb->s_cc |= oldEM;
479157167Srwatson					    m_freem(m0);
480157167Srwatson					    return (ENOBUFS);
481157167Srwatson					}
48211819Sjulian					if (cb->s_flags & SF_NEWCALL) {
48311819Sjulian					    struct mbuf *mm = m;
48411819Sjulian					    spx_newchecks[7]++;
48525652Sjhay					    while (mm != NULL) {
48611819Sjulian						mm->m_flags &= ~M_EOR;
48711819Sjulian						mm = mm->m_next;
48811819Sjulian					    }
48911819Sjulian					}
49011819Sjulian					error = spx_output(cb, m);
49111819Sjulian					if (error) {
49211819Sjulian						cb->s_cc |= oldEM;
49311819Sjulian						m_freem(m0);
49425652Sjhay						return (error);
49511819Sjulian					}
49611819Sjulian					m_adj(m0, mtu);
49711819Sjulian					len -= mtu;
49811819Sjulian				}
49911819Sjulian				cb->s_cc |= oldEM;
50011819Sjulian			}
50111819Sjulian		}
502157094Srwatson
50311819Sjulian		/*
50411819Sjulian		 * Force length even, by adding a "garbage byte" if
50511819Sjulian		 * necessary.
50611819Sjulian		 */
50711819Sjulian		if (len & 1) {
50811819Sjulian			m = mprev;
50911819Sjulian			if (M_TRAILINGSPACE(m) >= 1)
51011819Sjulian				m->m_len++;
51111819Sjulian			else {
512243882Sglebius				struct mbuf *m1 = m_get(M_NOWAIT, MT_DATA);
51311819Sjulian
51425652Sjhay				if (m1 == NULL) {
51511819Sjulian					m_freem(m0);
51611819Sjulian					return (ENOBUFS);
51711819Sjulian				}
51811819Sjulian				m1->m_len = 1;
51911819Sjulian				*(mtod(m1, u_char *)) = 0;
52011819Sjulian				m->m_next = m1;
52111819Sjulian			}
52211819Sjulian		}
523243882Sglebius		m = m_gethdr(M_NOWAIT, MT_DATA);
52425652Sjhay		if (m == NULL) {
52511819Sjulian			m_freem(m0);
52611819Sjulian			return (ENOBUFS);
52711819Sjulian		}
528157094Srwatson
52911819Sjulian		/*
530157094Srwatson		 * Fill in mbuf with extended SP header and addresses and
531157094Srwatson		 * length put into network format.
53211819Sjulian		 */
53325652Sjhay		MH_ALIGN(m, sizeof(struct spx));
53425652Sjhay		m->m_len = sizeof(struct spx);
53511819Sjulian		m->m_next = m0;
53611819Sjulian		si = mtod(m, struct spx *);
537192754Srwatson		si->si_i = cb->s_ipx;
53811819Sjulian		si->si_s = cb->s_shdr;
53911819Sjulian		if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) {
540157067Srwatson			struct spxhdr *sh;
54125652Sjhay			if (m0->m_len < sizeof(*sh)) {
54211819Sjulian				if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) {
54325652Sjhay					m_free(m);
54411819Sjulian					m_freem(m0);
54511819Sjulian					return (EINVAL);
54611819Sjulian				}
54711819Sjulian				m->m_next = m0;
54811819Sjulian			}
54911819Sjulian			sh = mtod(m0, struct spxhdr *);
55011819Sjulian			si->si_dt = sh->spx_dt;
55111819Sjulian			si->si_cc |= sh->spx_cc & SPX_EM;
55225652Sjhay			m0->m_len -= sizeof(*sh);
55325652Sjhay			m0->m_data += sizeof(*sh);
55425652Sjhay			len -= sizeof(*sh);
55511819Sjulian		}
55611819Sjulian		len += sizeof(*si);
55711819Sjulian		if ((cb->s_flags2 & SF_NEWCALL) && recordp) {
55825652Sjhay			si->si_cc |= SPX_EM;
55911819Sjulian			spx_newchecks[8]++;
56011819Sjulian		}
56111819Sjulian		if (cb->s_oobflags & SF_SOOB) {
56211819Sjulian			/*
563157094Srwatson			 * Per jqj@cornell: Make sure OB packets convey
564157094Srwatson			 * exactly 1 byte.  If the packet is 1 byte or
565157094Srwatson			 * larger, we have already guaranted there to be at
566157094Srwatson			 * least one garbage byte for the checksum, and extra
567157094Srwatson			 * bytes shouldn't hurt!
56811819Sjulian			 */
56911819Sjulian			if (len > sizeof(*si)) {
57011819Sjulian				si->si_cc |= SPX_OB;
57111819Sjulian				len = (1 + sizeof(*si));
57211819Sjulian			}
57311819Sjulian		}
57411819Sjulian		si->si_len = htons((u_short)len);
57511819Sjulian		m->m_pkthdr.len = ((len - 1) | 1) + 1;
576157094Srwatson
57711819Sjulian		/*
578157094Srwatson		 * Queue stuff up for output.
57911819Sjulian		 */
58011819Sjulian		sbappendrecord(sb, m);
58111819Sjulian		cb->s_seq++;
58211819Sjulian	}
58311819Sjulian#ifdef notdef
58411819Sjulian	idle = (cb->s_smax == (cb->s_rack - 1));
58511819Sjulian#endif
58611819Sjulianagain:
58711819Sjulian	sendalot = 0;
58811819Sjulian	off = cb->s_snxt - cb->s_rack;
58925652Sjhay	win = min(cb->s_swnd, (cb->s_cwnd / CUNIT));
59011819Sjulian
59111819Sjulian	/*
592157094Srwatson	 * If in persist timeout with window of 0, send a probe.  Otherwise,
593179408Srwatson	 * if window is small but non-zero and timer expired, send what we
594179408Srwatson	 * can and go into transmit state.
59511819Sjulian	 */
59611819Sjulian	if (cb->s_force == 1 + SPXT_PERSIST) {
59711819Sjulian		if (win != 0) {
59811819Sjulian			cb->s_timer[SPXT_PERSIST] = 0;
59911819Sjulian			cb->s_rxtshift = 0;
60011819Sjulian		}
60111819Sjulian	}
60211819Sjulian	span = cb->s_seq - cb->s_rack;
60311819Sjulian	len = min(span, win) - off;
60411819Sjulian
60511819Sjulian	if (len < 0) {
60611819Sjulian		/*
607157094Srwatson		 * Window shrank after we went into it.  If window shrank to
608157094Srwatson		 * 0, cancel pending restransmission and pull s_snxt back to
609157094Srwatson		 * (closed) window.  We will enter persist state below.  If
610157094Srwatson		 * the widndow didn't close completely, just wait for an ACK.
61111819Sjulian		 */
61211819Sjulian		len = 0;
61311819Sjulian		if (win == 0) {
61411819Sjulian			cb->s_timer[SPXT_REXMT] = 0;
61511819Sjulian			cb->s_snxt = cb->s_rack;
61611819Sjulian		}
61711819Sjulian	}
61811819Sjulian	if (len > 1)
61911819Sjulian		sendalot = 1;
62011819Sjulian	rcv_win = sbspace(&so->so_rcv);
62111819Sjulian
62211819Sjulian	/*
62311819Sjulian	 * Send if we owe peer an ACK.
62411819Sjulian	 */
62511819Sjulian	if (cb->s_oobflags & SF_SOOB) {
62611819Sjulian		/*
627157094Srwatson		 * Must transmit this out of band packet.
62811819Sjulian		 */
62911819Sjulian		cb->s_oobflags &= ~ SF_SOOB;
63011819Sjulian		sendalot = 1;
63111819Sjulian		spxstat.spxs_sndurg++;
63211819Sjulian		goto found;
63311819Sjulian	}
63411819Sjulian	if (cb->s_flags & SF_ACKNOW)
63511819Sjulian		goto send;
63611819Sjulian	if (cb->s_state < TCPS_ESTABLISHED)
63711819Sjulian		goto send;
638157094Srwatson
63911819Sjulian	/*
640157094Srwatson	 * Silly window can't happen in spx.  Code from TCP deleted.
64111819Sjulian	 */
64211819Sjulian	if (len)
64311819Sjulian		goto send;
644157094Srwatson
64511819Sjulian	/*
646157094Srwatson	 * Compare available window to amount of window known to peer (as
647157094Srwatson	 * advertised window less next expected input.)  If the difference is
648157094Srwatson	 * at least two packets or at least 35% of the mximum possible
649157094Srwatson	 * window, then want to send a window update to peer.
65011819Sjulian	 */
65111819Sjulian	if (rcv_win > 0) {
65211819Sjulian		u_short delta =  1 + cb->s_alo - cb->s_ack;
65311819Sjulian		int adv = rcv_win - (delta * cb->s_mtu);
654139584Srwatson
65511819Sjulian		if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) ||
65611819Sjulian		    (100 * adv / so->so_rcv.sb_hiwat >= 35)) {
65711819Sjulian			spxstat.spxs_sndwinup++;
65811819Sjulian			cb->s_flags |= SF_ACKNOW;
65911819Sjulian			goto send;
66011819Sjulian		}
66111819Sjulian
66211819Sjulian	}
663157094Srwatson
66411819Sjulian	/*
665157094Srwatson	 * Many comments from tcp_output.c are appropriate here including ...
66611819Sjulian	 * If send window is too small, there is data to transmit, and no
667157094Srwatson	 * retransmit or persist is pending, then go to persist state.  If
668157094Srwatson	 * nothing happens soon, send when timer expires: if window is
669179408Srwatson	 * non-zero, transmit what we can, otherwise send a probe.
67011819Sjulian	 */
67111819Sjulian	if (so->so_snd.sb_cc && cb->s_timer[SPXT_REXMT] == 0 &&
672157128Srwatson	    cb->s_timer[SPXT_PERSIST] == 0) {
673157128Srwatson		cb->s_rxtshift = 0;
674157128Srwatson		spx_setpersist(cb);
67511819Sjulian	}
676157094Srwatson
67711819Sjulian	/*
67811819Sjulian	 * No reason to send a packet, just return.
67911819Sjulian	 */
68011819Sjulian	cb->s_outx = 1;
68111819Sjulian	return (0);
68211819Sjulian
68311819Sjuliansend:
68411819Sjulian	/*
68511819Sjulian	 * Find requested packet.
68611819Sjulian	 */
687192755Srwatson	si = NULL;
688192756Srwatson	m = NULL;
68911819Sjulian	if (len > 0) {
69011819Sjulian		cb->s_want = cb->s_snxt;
691192748Srwatson		for (m = sb->sb_mb; m != NULL; m = m->m_nextpkt) {
69211819Sjulian			si = mtod(m, struct spx *);
69311819Sjulian			if (SSEQ_LEQ(cb->s_snxt, si->si_seq))
69411819Sjulian				break;
69511819Sjulian		}
69611819Sjulian	found:
69725652Sjhay		if (si != NULL) {
698192756Srwatson			if (si->si_seq != cb->s_snxt) {
699192756Srwatson				spxstat.spxs_sndvoid++;
700192756Srwatson				si = NULL;
701192756Srwatson				m = NULL;
702192756Srwatson			} else
703192756Srwatson				cb->s_snxt++;
70411819Sjulian		}
70511819Sjulian	}
706157094Srwatson
70711819Sjulian	/*
708157094Srwatson	 * Update window.
70911819Sjulian	 */
71011819Sjulian	if (rcv_win < 0)
71111819Sjulian		rcv_win = 0;
71211819Sjulian	alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu));
713139584Srwatson	if (SSEQ_LT(alo, cb->s_alo))
71411819Sjulian		alo = cb->s_alo;
71511819Sjulian
716192756Srwatson	if (m != NULL) {
71711819Sjulian		/*
718157094Srwatson		 * Must make a copy of this packet for ipx_output to monkey
719157094Srwatson		 * with.
72011819Sjulian		 */
721192756Srwatson		m = m_copy(m, 0, M_COPYALL);
722157094Srwatson		if (m == NULL)
72311819Sjulian			return (ENOBUFS);
72411819Sjulian		si = mtod(m, struct spx *);
72511819Sjulian		if (SSEQ_LT(si->si_seq, cb->s_smax))
72611819Sjulian			spxstat.spxs_sndrexmitpack++;
72711819Sjulian		else
72811819Sjulian			spxstat.spxs_sndpack++;
72911819Sjulian	} else if (cb->s_force || cb->s_flags & SF_ACKNOW) {
73011819Sjulian		/*
731157094Srwatson		 * Must send an acknowledgement or a probe.
73211819Sjulian		 */
73311819Sjulian		if (cb->s_force)
73411819Sjulian			spxstat.spxs_sndprobe++;
73511819Sjulian		if (cb->s_flags & SF_ACKNOW)
73611819Sjulian			spxstat.spxs_sndacks++;
737243882Sglebius		m = m_gethdr(M_NOWAIT, MT_DATA);
73825652Sjhay		if (m == NULL)
73911819Sjulian			return (ENOBUFS);
740157094Srwatson
74111819Sjulian		/*
742157094Srwatson		 * Fill in mbuf with extended SP header and addresses and
743157094Srwatson		 * length put into network format.
74411819Sjulian		 */
74525652Sjhay		MH_ALIGN(m, sizeof(struct spx));
74625652Sjhay		m->m_len = sizeof(*si);
74725652Sjhay		m->m_pkthdr.len = sizeof(*si);
74811819Sjulian		si = mtod(m, struct spx *);
749192754Srwatson		si->si_i = cb->s_ipx;
75011819Sjulian		si->si_s = cb->s_shdr;
75111819Sjulian		si->si_seq = cb->s_smax + 1;
75225652Sjhay		si->si_len = htons(sizeof(*si));
75311819Sjulian		si->si_cc |= SPX_SP;
75411819Sjulian	} else {
75511819Sjulian		cb->s_outx = 3;
75697658Stanimura		if (so->so_options & SO_DEBUG || traceallspxs)
75711819Sjulian			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
75811819Sjulian		return (0);
75911819Sjulian	}
760179408Srwatson
76111819Sjulian	/*
76211819Sjulian	 * Stuff checksum and output datagram.
76311819Sjulian	 */
76411819Sjulian	if ((si->si_cc & SPX_SP) == 0) {
76511819Sjulian		if (cb->s_force != (1 + SPXT_PERSIST) ||
76611819Sjulian		    cb->s_timer[SPXT_PERSIST] == 0) {
76711819Sjulian			/*
768139584Srwatson			 * If this is a new packet and we are not currently
76911819Sjulian			 * timing anything, time this one.
77011819Sjulian			 */
77111819Sjulian			if (SSEQ_LT(cb->s_smax, si->si_seq)) {
77211819Sjulian				cb->s_smax = si->si_seq;
77311819Sjulian				if (cb->s_rtt == 0) {
77411819Sjulian					spxstat.spxs_segstimed++;
77511819Sjulian					cb->s_rtseq = si->si_seq;
77611819Sjulian					cb->s_rtt = 1;
77711819Sjulian				}
77811819Sjulian			}
779157094Srwatson
78011819Sjulian			/*
781157094Srwatson			 * Set rexmt timer if not currently set, initial
782157094Srwatson			 * value for retransmit timer is smoothed round-trip
783157094Srwatson			 * time + 2 * round-trip time variance.  Initialize
784157094Srwatson			 * shift counter which is used for backoff of
785157094Srwatson			 * retransmit time.
78611819Sjulian			 */
78711819Sjulian			if (cb->s_timer[SPXT_REXMT] == 0 &&
78811819Sjulian			    cb->s_snxt != cb->s_rack) {
78911819Sjulian				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
79011819Sjulian				if (cb->s_timer[SPXT_PERSIST]) {
79111819Sjulian					cb->s_timer[SPXT_PERSIST] = 0;
79211819Sjulian					cb->s_rxtshift = 0;
79311819Sjulian				}
79411819Sjulian			}
795157094Srwatson		} else if (SSEQ_LT(cb->s_smax, si->si_seq))
79611819Sjulian			cb->s_smax = si->si_seq;
79711819Sjulian	} else if (cb->s_state < TCPS_ESTABLISHED) {
79811819Sjulian		if (cb->s_rtt == 0)
79911819Sjulian			cb->s_rtt = 1; /* Time initial handshake */
80011819Sjulian		if (cb->s_timer[SPXT_REXMT] == 0)
80111819Sjulian			cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
80211819Sjulian	}
80311819Sjulian
804157094Srwatson	/*
805157094Srwatson	 * Do not request acks when we ack their data packets or when we do a
806157094Srwatson	 * gratuitous window update.
807157094Srwatson	 */
808157094Srwatson	if (((si->si_cc & SPX_SP) == 0) || cb->s_force)
809157094Srwatson		si->si_cc |= SPX_SA;
810157094Srwatson	si->si_seq = htons(si->si_seq);
811157094Srwatson	si->si_alo = htons(alo);
812157094Srwatson	si->si_ack = htons(cb->s_ack);
81311819Sjulian
814157094Srwatson	if (ipxcksum)
815157094Srwatson		si->si_sum = ipx_cksum(m, ntohs(si->si_len));
816157094Srwatson	else
817157094Srwatson		si->si_sum = 0xffff;
81811819Sjulian
819157094Srwatson	cb->s_outx = 4;
820157094Srwatson	if (so->so_options & SO_DEBUG || traceallspxs)
821157094Srwatson		spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
822157094Srwatson
823194545Srwatson#ifdef MAC
824194545Srwatson	mac_socket_create_mbuf(so, m);
825194545Srwatson#endif
826194545Srwatson
827157094Srwatson	if (so->so_options & SO_DONTROUTE)
828157094Srwatson		error = ipx_outputfl(m, NULL, IPX_ROUTETOIF);
829157094Srwatson	else
830157094Srwatson		error = ipx_outputfl(m, &cb->s_ipxpcb->ipxp_route, 0);
831157094Srwatson	if (error)
83211819Sjulian		return (error);
83311819Sjulian	spxstat.spxs_sndtotal++;
834157094Srwatson
83511819Sjulian	/*
836157094Srwatson	 * Data sent (as far as we can tell).  If this advertises a larger
837157094Srwatson	 * window than any other segment, then remember the size of the
838157094Srwatson	 * advertized window.  Any pending ACK has now been sent.
83911819Sjulian	 */
84011819Sjulian	cb->s_force = 0;
84111819Sjulian	cb->s_flags &= ~(SF_ACKNOW|SF_DELACK);
84211819Sjulian	if (SSEQ_GT(alo, cb->s_alo))
84311819Sjulian		cb->s_alo = alo;
84411819Sjulian	if (sendalot)
84511819Sjulian		goto again;
84611819Sjulian	cb->s_outx = 5;
84711819Sjulian	return (0);
84811819Sjulian}
84911819Sjulian
85033181Seivindstatic int spx_do_persist_panics = 0;
85111819Sjulian
85225652Sjhaystatic void
853157067Srwatsonspx_setpersist(struct spxpcb *cb)
85411819Sjulian{
855157067Srwatson	int t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
85611819Sjulian
857139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
858139932Srwatson
85911819Sjulian	if (cb->s_timer[SPXT_REXMT] && spx_do_persist_panics)
86011819Sjulian		panic("spx_output REXMT");
861157094Srwatson
86211819Sjulian	/*
86311819Sjulian	 * Start/restart persistance timer.
86411819Sjulian	 */
86511819Sjulian	SPXT_RANGESET(cb->s_timer[SPXT_PERSIST],
86611819Sjulian	    t*spx_backoff[cb->s_rxtshift],
86711819Sjulian	    SPXTV_PERSMIN, SPXTV_PERSMAX);
86811819Sjulian	if (cb->s_rxtshift < SPX_MAXRXTSHIFT)
86911819Sjulian		cb->s_rxtshift++;
87011819Sjulian}
87125652Sjhay
87211819Sjulianint
873157067Srwatsonspx_ctloutput(struct socket *so, struct sockopt *sopt)
87411819Sjulian{
875157125Srwatson	struct spxhdr spxhdr;
876157125Srwatson	struct ipxpcb *ipxp;
877157067Srwatson	struct spxpcb *cb;
87838482Swollman	int mask, error;
87938482Swollman	short soptval;
88038482Swollman	u_short usoptval;
88138482Swollman	int optval;
88211819Sjulian
883157128Srwatson	ipxp = sotoipxpcb(so);
884157128Srwatson	KASSERT(ipxp != NULL, ("spx_ctloutput: ipxp == NULL"));
885157128Srwatson
886157094Srwatson	/*
887157094Srwatson	 * This will have to be changed when we do more general stacking of
888157094Srwatson	 * protocols.
889157094Srwatson	 */
890157094Srwatson	if (sopt->sopt_level != IPXPROTO_SPX)
89138482Swollman		return (ipx_ctloutput(so, sopt));
892157125Srwatson
893157128Srwatson	IPX_LOCK(ipxp);
894157128Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
895157128Srwatson		IPX_UNLOCK(ipxp);
896157128Srwatson		return (ECONNRESET);
897157128Srwatson	}
898157125Srwatson
899157125Srwatson	IPX_LOCK(ipxp);
900157125Srwatson	cb = ipxtospxpcb(ipxp);
901157094Srwatson	KASSERT(cb != NULL, ("spx_ctloutput: cb == NULL"));
90211819Sjulian
903157125Srwatson	error = 0;
90438482Swollman	switch (sopt->sopt_dir) {
90538482Swollman	case SOPT_GET:
90638482Swollman		switch (sopt->sopt_name) {
90711819Sjulian		case SO_HEADERS_ON_INPUT:
90811819Sjulian			mask = SF_HI;
90911819Sjulian			goto get_flags;
91011819Sjulian
91111819Sjulian		case SO_HEADERS_ON_OUTPUT:
91211819Sjulian			mask = SF_HO;
91311819Sjulian		get_flags:
91438482Swollman			soptval = cb->s_flags & mask;
915157125Srwatson			IPX_UNLOCK(ipxp);
916157125Srwatson			error = sooptcopyout(sopt, &soptval,
917157125Srwatson			    sizeof(soptval));
91811819Sjulian			break;
91911819Sjulian
92011819Sjulian		case SO_MTU:
92138482Swollman			usoptval = cb->s_mtu;
922157125Srwatson			IPX_UNLOCK(ipxp);
923157125Srwatson			error = sooptcopyout(sopt, &usoptval,
924157125Srwatson			    sizeof(usoptval));
92511819Sjulian			break;
92611819Sjulian
92711819Sjulian		case SO_LAST_HEADER:
928157125Srwatson			spxhdr = cb->s_rhdr;
929157125Srwatson			IPX_UNLOCK(ipxp);
930157125Srwatson			error = sooptcopyout(sopt, &spxhdr, sizeof(spxhdr));
93111819Sjulian			break;
93211819Sjulian
93311819Sjulian		case SO_DEFAULT_HEADERS:
934157125Srwatson			spxhdr = cb->s_shdr;
935157125Srwatson			IPX_UNLOCK(ipxp);
936157125Srwatson			error = sooptcopyout(sopt, &spxhdr, sizeof(spxhdr));
93711819Sjulian			break;
93811819Sjulian
93911819Sjulian		default:
940157125Srwatson			IPX_UNLOCK(ipxp);
94138482Swollman			error = ENOPROTOOPT;
94211819Sjulian		}
94311819Sjulian		break;
94411819Sjulian
94538482Swollman	case SOPT_SET:
946157094Srwatson		/*
947157094Srwatson		 * XXX Why are these shorts on get and ints on set?  That
948157094Srwatson		 * doesn't make any sense...
949157128Srwatson		 *
950157128Srwatson		 * XXXRW: Note, when we re-acquire the ipxp lock, we should
951157128Srwatson		 * re-check that it's not dropped.
952157094Srwatson		 */
953157125Srwatson		IPX_UNLOCK(ipxp);
95438482Swollman		switch (sopt->sopt_name) {
95511819Sjulian		case SO_HEADERS_ON_INPUT:
95611819Sjulian			mask = SF_HI;
95711819Sjulian			goto set_head;
95811819Sjulian
95911819Sjulian		case SO_HEADERS_ON_OUTPUT:
96011819Sjulian			mask = SF_HO;
96111819Sjulian		set_head:
96238482Swollman			error = sooptcopyin(sopt, &optval, sizeof optval,
96338482Swollman					    sizeof optval);
96438482Swollman			if (error)
96538482Swollman				break;
96638482Swollman
967139932Srwatson			IPX_LOCK(ipxp);
96811819Sjulian			if (cb->s_flags & SF_PI) {
96938482Swollman				if (optval)
97011819Sjulian					cb->s_flags |= mask;
97111819Sjulian				else
97211819Sjulian					cb->s_flags &= ~mask;
97311819Sjulian			} else error = EINVAL;
974139932Srwatson			IPX_UNLOCK(ipxp);
97511819Sjulian			break;
97611819Sjulian
97711819Sjulian		case SO_MTU:
97838482Swollman			error = sooptcopyin(sopt, &usoptval, sizeof usoptval,
97938482Swollman					    sizeof usoptval);
98038482Swollman			if (error)
98138482Swollman				break;
982139932Srwatson			/* Unlocked write. */
98338482Swollman			cb->s_mtu = usoptval;
98411819Sjulian			break;
98511819Sjulian
98611819Sjulian#ifdef SF_NEWCALL
98711819Sjulian		case SO_NEWCALL:
98838482Swollman			error = sooptcopyin(sopt, &optval, sizeof optval,
98938482Swollman					    sizeof optval);
99038482Swollman			if (error)
99138482Swollman				break;
992139932Srwatson			IPX_LOCK(ipxp);
99338482Swollman			if (optval) {
99411819Sjulian				cb->s_flags2 |= SF_NEWCALL;
99511819Sjulian				spx_newchecks[5]++;
99611819Sjulian			} else {
99711819Sjulian				cb->s_flags2 &= ~SF_NEWCALL;
99811819Sjulian				spx_newchecks[6]++;
99911819Sjulian			}
1000139932Srwatson			IPX_UNLOCK(ipxp);
100111819Sjulian			break;
100211819Sjulian#endif
100311819Sjulian
100411819Sjulian		case SO_DEFAULT_HEADERS:
100511819Sjulian			{
100638482Swollman				struct spxhdr sp;
100738482Swollman
100838482Swollman				error = sooptcopyin(sopt, &sp, sizeof sp,
100938482Swollman						    sizeof sp);
101038482Swollman				if (error)
101138482Swollman					break;
1012139932Srwatson				IPX_LOCK(ipxp);
101338482Swollman				cb->s_dt = sp.spx_dt;
101438482Swollman				cb->s_cc = sp.spx_cc & SPX_EM;
1015139932Srwatson				IPX_UNLOCK(ipxp);
101611819Sjulian			}
101711819Sjulian			break;
101811819Sjulian
101911819Sjulian		default:
102038482Swollman			error = ENOPROTOOPT;
102111819Sjulian		}
102211819Sjulian		break;
1023157125Srwatson
1024157125Srwatson	default:
1025157125Srwatson		panic("spx_ctloutput: bad socket option direction");
102611819Sjulian	}
102738482Swollman	return (error);
102811819Sjulian}
102911819Sjulian
1030157366Srwatsonstatic void
1031157067Srwatsonspx_usr_abort(struct socket *so)
103211819Sjulian{
103324659Sjhay	struct ipxpcb *ipxp;
103424659Sjhay	struct spxpcb *cb;
103511819Sjulian
103624659Sjhay	ipxp = sotoipxpcb(so);
1037157094Srwatson	KASSERT(ipxp != NULL, ("spx_usr_abort: ipxp == NULL"));
1038157094Srwatson
103924659Sjhay	cb = ipxtospxpcb(ipxp);
1040157094Srwatson	KASSERT(cb != NULL, ("spx_usr_abort: cb == NULL"));
104111819Sjulian
1042139932Srwatson	IPX_LIST_LOCK();
1043139932Srwatson	IPX_LOCK(ipxp);
104424659Sjhay	spx_drop(cb, ECONNABORTED);
1045160549Srwatson	IPX_UNLOCK(ipxp);
1046139932Srwatson	IPX_LIST_UNLOCK();
104724659Sjhay}
104811819Sjulian
104924659Sjhay/*
1050157094Srwatson * Accept a connection.  Essentially all the work is done at higher levels;
1051157094Srwatson * just return the address of the peer, storing through addr.
105224659Sjhay */
105324659Sjhaystatic int
1054157067Srwatsonspx_accept(struct socket *so, struct sockaddr **nam)
105524659Sjhay{
105624659Sjhay	struct ipxpcb *ipxp;
105728270Swollman	struct sockaddr_ipx *sipx, ssipx;
105811819Sjulian
105924659Sjhay	ipxp = sotoipxpcb(so);
1060157154Srwatson	KASSERT(ipxp != NULL, ("spx_accept: ipxp == NULL"));
1061157094Srwatson
106228270Swollman	sipx = &ssipx;
106328270Swollman	bzero(sipx, sizeof *sipx);
106428270Swollman	sipx->sipx_len = sizeof *sipx;
106524659Sjhay	sipx->sipx_family = AF_IPX;
1066139932Srwatson	IPX_LOCK(ipxp);
106724659Sjhay	sipx->sipx_addr = ipxp->ipxp_faddr;
1068139932Srwatson	IPX_UNLOCK(ipxp);
1069139932Srwatson	*nam = sodupsockaddr((struct sockaddr *)sipx, M_WAITOK);
107024659Sjhay	return (0);
107124659Sjhay}
107224659Sjhay
107324659Sjhaystatic int
1074157067Srwatsonspx_attach(struct socket *so, int proto, struct thread *td)
107524659Sjhay{
107624659Sjhay	struct ipxpcb *ipxp;
107724659Sjhay	struct spxpcb *cb;
107824659Sjhay	struct mbuf *mm;
107924659Sjhay	struct sockbuf *sb;
1080139932Srwatson	int error;
108124659Sjhay
108224659Sjhay	ipxp = sotoipxpcb(so);
1083157094Srwatson	KASSERT(ipxp == NULL, ("spx_attach: ipxp != NULL"));
108424659Sjhay
108524659Sjhay	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
108624659Sjhay		error = soreserve(so, (u_long) 3072, (u_long) 3072);
108711819Sjulian		if (error)
1088157127Srwatson			return (error);
108924659Sjhay	}
109011819Sjulian
1091184205Sdes	cb = malloc(sizeof *cb, M_PCB, M_NOWAIT | M_ZERO);
1092157127Srwatson	if (cb == NULL)
1093157127Srwatson		return (ENOBUFS);
1094243882Sglebius	mm = m_getclr(M_NOWAIT, MT_DATA);
109524659Sjhay	if (mm == NULL) {
1096184205Sdes		free(cb, M_PCB);
1097157127Srwatson		return (ENOBUFS);
109824659Sjhay	}
1099157127Srwatson
1100157127Srwatson	IPX_LIST_LOCK();
1101157127Srwatson	error = ipx_pcballoc(so, &ipxpcb_list, td);
1102157127Srwatson	if (error) {
1103157127Srwatson		IPX_LIST_UNLOCK();
1104157127Srwatson		m_free(mm);
1105184205Sdes		free(cb, M_PCB);
1106157127Srwatson		return (error);
1107157127Srwatson	}
1108157127Srwatson	ipxp = sotoipxpcb(so);
1109157145Srwatson	ipxp->ipxp_flags |= IPXP_SPX;
1110157127Srwatson
111124659Sjhay	cb->s_state = TCPS_LISTEN;
111224659Sjhay	cb->s_smax = -1;
111324659Sjhay	cb->s_swl1 = -1;
1114192753Srwatson	spx_reass_init(cb);
111524659Sjhay	cb->s_ipxpcb = ipxp;
111625652Sjhay	cb->s_mtu = 576 - sizeof(struct spx);
1117157094Srwatson	sb = &so->so_snd;
111824659Sjhay	cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu;
111924659Sjhay	cb->s_ssthresh = cb->s_cwnd;
112025652Sjhay	cb->s_cwmx = sbspace(sb) * CUNIT / (2 * sizeof(struct spx));
1121179408Srwatson
1122157094Srwatson	/*
1123157094Srwatson	 * Above is recomputed when connecting to account for changed
1124157094Srwatson	 * buffering or mtu's.
1125157094Srwatson	 */
112624659Sjhay	cb->s_rtt = SPXTV_SRTTBASE;
112724659Sjhay	cb->s_rttvar = SPXTV_SRTTDFLT << 2;
112824659Sjhay	SPXT_RANGESET(cb->s_rxtcur,
112924659Sjhay	    ((SPXTV_SRTTBASE >> 2) + (SPXTV_SRTTDFLT << 2)) >> 1,
113024659Sjhay	    SPXTV_MIN, SPXTV_REXMTMAX);
1131139584Srwatson	ipxp->ipxp_pcb = (caddr_t)cb;
1132139932Srwatson	IPX_LIST_UNLOCK();
1133157128Srwatson	return (0);
113424659Sjhay}
113511819Sjulian
1136157128Srwatsonstatic void
1137157128Srwatsonspx_pcbdetach(struct ipxpcb *ipxp)
1138157128Srwatson{
1139157128Srwatson	struct spxpcb *cb;
1140157128Srwatson
1141157128Srwatson	IPX_LOCK_ASSERT(ipxp);
1142157128Srwatson
1143157128Srwatson	cb = ipxtospxpcb(ipxp);
1144157128Srwatson	KASSERT(cb != NULL, ("spx_pcbdetach: cb == NULL"));
1145157128Srwatson
1146192753Srwatson	spx_reass_flush(cb);
1147184205Sdes	free(cb, M_PCB);
1148157128Srwatson	ipxp->ipxp_pcb = NULL;
1149157128Srwatson}
1150157128Srwatson
115124659Sjhaystatic int
1152157067Srwatsonspx_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
1153139584Srwatson{
115424659Sjhay	struct ipxpcb *ipxp;
1155139932Srwatson	int error;
115611819Sjulian
115724659Sjhay	ipxp = sotoipxpcb(so);
1158157094Srwatson	KASSERT(ipxp != NULL, ("spx_bind: ipxp == NULL"));
115911819Sjulian
1160139932Srwatson	IPX_LIST_LOCK();
1161139932Srwatson	IPX_LOCK(ipxp);
1162157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1163157153Srwatson		error = EINVAL;
1164157153Srwatson		goto out;
1165157153Srwatson	}
1166139932Srwatson	error = ipx_pcbbind(ipxp, nam, td);
1167157153Srwatsonout:
1168139932Srwatson	IPX_UNLOCK(ipxp);
1169139932Srwatson	IPX_LIST_UNLOCK();
1170139932Srwatson	return (error);
1171139584Srwatson}
1172139584Srwatson
1173160549Srwatsonstatic void
1174160549Srwatsonspx_usr_close(struct socket *so)
1175160549Srwatson{
1176160549Srwatson	struct ipxpcb *ipxp;
1177160549Srwatson	struct spxpcb *cb;
1178160549Srwatson
1179160549Srwatson	ipxp = sotoipxpcb(so);
1180160549Srwatson	KASSERT(ipxp != NULL, ("spx_usr_close: ipxp == NULL"));
1181160549Srwatson
1182160549Srwatson	cb = ipxtospxpcb(ipxp);
1183160549Srwatson	KASSERT(cb != NULL, ("spx_usr_close: cb == NULL"));
1184160549Srwatson
1185160549Srwatson	IPX_LIST_LOCK();
1186160549Srwatson	IPX_LOCK(ipxp);
1187160549Srwatson	if (cb->s_state > TCPS_LISTEN)
1188160549Srwatson		spx_disconnect(cb);
1189160549Srwatson	else
1190160549Srwatson		spx_close(cb);
1191160549Srwatson	IPX_UNLOCK(ipxp);
1192160549Srwatson	IPX_LIST_UNLOCK();
1193160549Srwatson}
1194160549Srwatson
119524659Sjhay/*
1196157094Srwatson * Initiate connection to peer.  Enter SYN_SENT state, and mark socket as
1197157094Srwatson * connecting.  Start keep-alive timer, setup prototype header, send initial
1198157094Srwatson * system packet requesting connection.
119924659Sjhay */
120024659Sjhaystatic int
1201157067Srwatsonspx_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
120224659Sjhay{
120324659Sjhay	struct ipxpcb *ipxp;
120424659Sjhay	struct spxpcb *cb;
1205139932Srwatson	int error;
120611819Sjulian
120724659Sjhay	ipxp = sotoipxpcb(so);
1208157094Srwatson	KASSERT(ipxp != NULL, ("spx_connect: ipxp == NULL"));
1209157094Srwatson
121024659Sjhay	cb = ipxtospxpcb(ipxp);
1211157094Srwatson	KASSERT(cb != NULL, ("spx_connect: cb == NULL"));
121224659Sjhay
1213139932Srwatson	IPX_LIST_LOCK();
1214139932Srwatson	IPX_LOCK(ipxp);
1215157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1216157153Srwatson		error = EINVAL;
1217157153Srwatson		goto spx_connect_end;
1218157153Srwatson	}
121924659Sjhay	if (ipxp->ipxp_lport == 0) {
1220139579Srwatson		error = ipx_pcbbind(ipxp, NULL, td);
122124659Sjhay		if (error)
122224659Sjhay			goto spx_connect_end;
122324659Sjhay	}
122483366Sjulian	error = ipx_pcbconnect(ipxp, nam, td);
122524659Sjhay	if (error)
122624659Sjhay		goto spx_connect_end;
122724659Sjhay	soisconnecting(so);
122824659Sjhay	spxstat.spxs_connattempt++;
122924659Sjhay	cb->s_state = TCPS_SYN_SENT;
123024659Sjhay	cb->s_did = 0;
123124659Sjhay	spx_template(cb);
123224659Sjhay	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
123324659Sjhay	cb->s_force = 1 + SPXTV_KEEP;
1234179408Srwatson
123511819Sjulian	/*
1236157094Srwatson	 * Other party is required to respond to the port I send from, but he
1237157094Srwatson	 * is not required to answer from where I am sending to, so allow
1238157094Srwatson	 * wildcarding.  Original port I am sending to is still saved in
123924659Sjhay	 * cb->s_dport.
124011819Sjulian	 */
124124659Sjhay	ipxp->ipxp_fport = 0;
1242139579Srwatson	error = spx_output(cb, NULL);
124324659Sjhayspx_connect_end:
1244139932Srwatson	IPX_UNLOCK(ipxp);
1245139932Srwatson	IPX_LIST_UNLOCK();
124624659Sjhay	return (error);
124724659Sjhay}
124811819Sjulian
1249157370Srwatsonstatic void
1250157067Srwatsonspx_detach(struct socket *so)
125124659Sjhay{
125224659Sjhay	struct ipxpcb *ipxp;
125324659Sjhay	struct spxpcb *cb;
125411819Sjulian
1255160549Srwatson	/*
1256160549Srwatson	 * XXXRW: Should assert appropriately detached.
1257160549Srwatson	 */
125824659Sjhay	ipxp = sotoipxpcb(so);
1259157094Srwatson	KASSERT(ipxp != NULL, ("spx_detach: ipxp == NULL"));
1260157094Srwatson
126124659Sjhay	cb = ipxtospxpcb(ipxp);
1262157094Srwatson	KASSERT(cb != NULL, ("spx_detach: cb == NULL"));
126311819Sjulian
1264139932Srwatson	IPX_LIST_LOCK();
1265139932Srwatson	IPX_LOCK(ipxp);
1266157128Srwatson	spx_pcbdetach(ipxp);
1267192757Srwatson	ipx_pcbdetach(ipxp);
1268157128Srwatson	ipx_pcbfree(ipxp);
1269139932Srwatson	IPX_LIST_UNLOCK();
127024659Sjhay}
127111819Sjulian
127224659Sjhay/*
1273157094Srwatson * We may decide later to implement connection closing handshaking at the spx
1274157094Srwatson * level optionally.  Here is the hook to do it:
127524659Sjhay */
127624659Sjhaystatic int
1277157067Srwatsonspx_usr_disconnect(struct socket *so)
127824659Sjhay{
127924659Sjhay	struct ipxpcb *ipxp;
128024659Sjhay	struct spxpcb *cb;
1281157153Srwatson	int error;
128211819Sjulian
128324659Sjhay	ipxp = sotoipxpcb(so);
1284157094Srwatson	KASSERT(ipxp != NULL, ("spx_usr_disconnect: ipxp == NULL"));
1285157094Srwatson
128624659Sjhay	cb = ipxtospxpcb(ipxp);
1287157094Srwatson	KASSERT(cb != NULL, ("spx_usr_disconnect: cb == NULL"));
128811819Sjulian
1289139932Srwatson	IPX_LIST_LOCK();
1290139932Srwatson	IPX_LOCK(ipxp);
1291157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1292157153Srwatson		error = EINVAL;
1293157153Srwatson		goto out;
1294157153Srwatson	}
129524659Sjhay	spx_disconnect(cb);
1296157153Srwatson	error = 0;
1297157153Srwatsonout:
1298157128Srwatson	IPX_UNLOCK(ipxp);
1299139932Srwatson	IPX_LIST_UNLOCK();
1300157153Srwatson	return (error);
130124659Sjhay}
130211819Sjulian
130324659Sjhaystatic int
1304157067Srwatsonspx_listen(struct socket *so, int backlog, struct thread *td)
130524659Sjhay{
130624659Sjhay	int error;
130724659Sjhay	struct ipxpcb *ipxp;
130824659Sjhay	struct spxpcb *cb;
130911819Sjulian
131024659Sjhay	error = 0;
131124659Sjhay	ipxp = sotoipxpcb(so);
1312157094Srwatson	KASSERT(ipxp != NULL, ("spx_listen: ipxp == NULL"));
1313157094Srwatson
131424659Sjhay	cb = ipxtospxpcb(ipxp);
1315157094Srwatson	KASSERT(cb != NULL, ("spx_listen: cb == NULL"));
131611819Sjulian
1317139932Srwatson	IPX_LIST_LOCK();
1318139932Srwatson	IPX_LOCK(ipxp);
1319157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1320157153Srwatson		error = EINVAL;
1321157153Srwatson		goto out;
1322157153Srwatson	}
1323142190Srwatson	SOCK_LOCK(so);
1324142190Srwatson	error = solisten_proto_check(so);
1325142190Srwatson	if (error == 0 && ipxp->ipxp_lport == 0)
1326139579Srwatson		error = ipx_pcbbind(ipxp, NULL, td);
1327142190Srwatson	if (error == 0) {
132824659Sjhay		cb->s_state = TCPS_LISTEN;
1329151888Srwatson		solisten_proto(so, backlog);
1330142190Srwatson	}
1331142190Srwatson	SOCK_UNLOCK(so);
1332157153Srwatsonout:
1333139932Srwatson	IPX_UNLOCK(ipxp);
1334139932Srwatson	IPX_LIST_UNLOCK();
133524659Sjhay	return (error);
133624659Sjhay}
133711819Sjulian
133824659Sjhay/*
1339157094Srwatson * After a receive, possibly send acknowledgment updating allocation.
134024659Sjhay */
134124659Sjhaystatic int
1342157067Srwatsonspx_rcvd(struct socket *so, int flags)
134324659Sjhay{
134424659Sjhay	struct ipxpcb *ipxp;
134524659Sjhay	struct spxpcb *cb;
1346157153Srwatson	int error;
134711819Sjulian
134824659Sjhay	ipxp = sotoipxpcb(so);
1349157094Srwatson	KASSERT(ipxp != NULL, ("spx_rcvd: ipxp == NULL"));
1350157094Srwatson
135124659Sjhay	cb = ipxtospxpcb(ipxp);
1352157094Srwatson	KASSERT(cb != NULL, ("spx_rcvd: cb == NULL"));
135311819Sjulian
1354139932Srwatson	IPX_LOCK(ipxp);
1355157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1356157153Srwatson		error = EINVAL;
1357157153Srwatson		goto out;
1358157153Srwatson	}
135924659Sjhay	cb->s_flags |= SF_RVD;
1360139579Srwatson	spx_output(cb, NULL);
136124659Sjhay	cb->s_flags &= ~SF_RVD;
1362157153Srwatson	error = 0;
1363157153Srwatsonout:
1364139932Srwatson	IPX_UNLOCK(ipxp);
1365157153Srwatson	return (error);
136624659Sjhay}
136711819Sjulian
136824659Sjhaystatic int
1369157067Srwatsonspx_rcvoob(struct socket *so, struct mbuf *m, int flags)
137024659Sjhay{
137124659Sjhay	struct ipxpcb *ipxp;
137224659Sjhay	struct spxpcb *cb;
1373157153Srwatson	int error;
137411819Sjulian
137524659Sjhay	ipxp = sotoipxpcb(so);
1376157094Srwatson	KASSERT(ipxp != NULL, ("spx_rcvoob: ipxp == NULL"));
1377157094Srwatson
137824659Sjhay	cb = ipxtospxpcb(ipxp);
1379157094Srwatson	KASSERT(cb != NULL, ("spx_rcvoob: cb == NULL"));
138011819Sjulian
1381157128Srwatson	IPX_LOCK(ipxp);
1382157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1383157153Srwatson		error = EINVAL;
1384157153Srwatson		goto out;
1385157153Srwatson	}
1386139591Srwatson	SOCKBUF_LOCK(&so->so_rcv);
138724659Sjhay	if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark ||
1388130480Srwatson	    (so->so_rcv.sb_state & SBS_RCVATMARK)) {
1389139591Srwatson		SOCKBUF_UNLOCK(&so->so_rcv);
139024659Sjhay		m->m_len = 1;
139124659Sjhay		*mtod(m, caddr_t) = cb->s_iobc;
1392157153Srwatson		error = 0;
1393157153Srwatson		goto out;
139411819Sjulian	}
1395139591Srwatson	SOCKBUF_UNLOCK(&so->so_rcv);
1396157153Srwatson	error = EINVAL;
1397157153Srwatsonout:
1398157128Srwatson	IPX_UNLOCK(ipxp);
1399157153Srwatson	return (error);
140024659Sjhay}
140124659Sjhay
140224659Sjhaystatic int
1403157067Srwatsonspx_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1404157067Srwatson    struct mbuf *controlp, struct thread *td)
140524659Sjhay{
140624659Sjhay	struct ipxpcb *ipxp;
140724659Sjhay	struct spxpcb *cb;
1408157153Srwatson	int error;
140924659Sjhay
141024659Sjhay	ipxp = sotoipxpcb(so);
1411157094Srwatson	KASSERT(ipxp != NULL, ("spx_send: ipxp == NULL"));
1412157094Srwatson
141324659Sjhay	cb = ipxtospxpcb(ipxp);
1414157094Srwatson	KASSERT(cb != NULL, ("spx_send: cb == NULL"));
141524659Sjhay
1416157094Srwatson	error = 0;
1417139932Srwatson	IPX_LOCK(ipxp);
1418157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1419157153Srwatson		error = ECONNRESET;
1420157153Srwatson		goto spx_send_end;
1421157153Srwatson	}
142224659Sjhay	if (flags & PRUS_OOB) {
142324659Sjhay		if (sbspace(&so->so_snd) < -512) {
142424659Sjhay			error = ENOBUFS;
142524659Sjhay			goto spx_send_end;
142624659Sjhay		}
142724659Sjhay		cb->s_oobflags |= SF_SOOB;
142824659Sjhay	}
142925652Sjhay	if (controlp != NULL) {
143024659Sjhay		u_short *p = mtod(controlp, u_short *);
143124659Sjhay		spx_newchecks[2]++;
143225652Sjhay		if ((p[0] == 5) && (p[1] == 1)) { /* XXXX, for testing */
143324659Sjhay			cb->s_shdr.spx_dt = *(u_char *)(&p[2]);
143424659Sjhay			spx_newchecks[3]++;
143524659Sjhay		}
143624659Sjhay		m_freem(controlp);
143724659Sjhay	}
143824659Sjhay	controlp = NULL;
143924659Sjhay	error = spx_output(cb, m);
144024659Sjhay	m = NULL;
144124659Sjhayspx_send_end:
1442139932Srwatson	IPX_UNLOCK(ipxp);
144311819Sjulian	if (controlp != NULL)
144411819Sjulian		m_freem(controlp);
144511819Sjulian	if (m != NULL)
144611819Sjulian		m_freem(m);
144711819Sjulian	return (error);
144811819Sjulian}
144911819Sjulian
145024659Sjhaystatic int
1451157067Srwatsonspx_shutdown(struct socket *so)
145224659Sjhay{
145324659Sjhay	struct ipxpcb *ipxp;
145424659Sjhay	struct spxpcb *cb;
1455157153Srwatson	int error;
145624659Sjhay
145724659Sjhay	ipxp = sotoipxpcb(so);
1458157094Srwatson	KASSERT(ipxp != NULL, ("spx_shutdown: ipxp == NULL"));
1459157094Srwatson
146024659Sjhay	cb = ipxtospxpcb(ipxp);
1461157094Srwatson	KASSERT(cb != NULL, ("spx_shutdown: cb == NULL"));
146224659Sjhay
146324659Sjhay	socantsendmore(so);
1464139932Srwatson	IPX_LIST_LOCK();
1465139932Srwatson	IPX_LOCK(ipxp);
1466157153Srwatson	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1467157153Srwatson		error = EINVAL;
1468157153Srwatson		goto out;
1469157153Srwatson	}
1470139931Srwatson	spx_usrclosed(cb);
1471157153Srwatson	error = 0;
1472157153Srwatsonout:
1473157128Srwatson	IPX_UNLOCK(ipxp);
1474139932Srwatson	IPX_LIST_UNLOCK();
1475157153Srwatson	return (error);
147624659Sjhay}
147724659Sjhay
147824659Sjhaystatic int
1479157067Srwatsonspx_sp_attach(struct socket *so, int proto, struct thread *td)
148011819Sjulian{
1481157127Srwatson	struct ipxpcb *ipxp;
1482157127Srwatson	struct spxpcb *cb;
148324659Sjhay	int error;
148411819Sjulian
1485157127Srwatson	KASSERT(so->so_pcb == NULL, ("spx_sp_attach: so_pcb != NULL"));
1486157127Srwatson
148783366Sjulian	error = spx_attach(so, proto, td);
1488157127Srwatson	if (error)
1489157127Srwatson		return (error);
1490157127Srwatson
1491157127Srwatson	ipxp = sotoipxpcb(so);
1492157127Srwatson	KASSERT(ipxp != NULL, ("spx_sp_attach: ipxp == NULL"));
1493157127Srwatson
1494157127Srwatson	cb = ipxtospxpcb(ipxp);
1495157127Srwatson	KASSERT(cb != NULL, ("spx_sp_attach: cb == NULL"));
1496157127Srwatson
1497157127Srwatson	IPX_LOCK(ipxp);
1498157127Srwatson	cb->s_flags |= (SF_HI | SF_HO | SF_PI);
1499157127Srwatson	IPX_UNLOCK(ipxp);
1500157127Srwatson	return (0);
150111819Sjulian}
150211819Sjulian
150311819Sjulian/*
1504157094Srwatson * Create template to be used to send spx packets on a connection.  Called
1505157094Srwatson * after host entry created, fills in a skeletal spx header (choosing
1506157094Srwatson * connection id), minimizing the amount of work necessary when the
1507157094Srwatson * connection is used.
150811819Sjulian */
150925652Sjhaystatic void
1510157067Srwatsonspx_template(struct spxpcb *cb)
151111819Sjulian{
1512157067Srwatson	struct ipxpcb *ipxp = cb->s_ipxpcb;
1513157067Srwatson	struct sockbuf *sb = &(ipxp->ipxp_socket->so_snd);
151411819Sjulian
1515139932Srwatson	IPX_LOCK_ASSERT(ipxp);
1516139932Srwatson
1517192754Srwatson	cb->s_ipx.ipx_pt = IPXPROTO_SPX;
1518192754Srwatson	cb->s_ipx.ipx_sna = ipxp->ipxp_laddr;
1519192754Srwatson	cb->s_ipx.ipx_dna = ipxp->ipxp_faddr;
1520157069Srwatson	SPX_LOCK();
152111819Sjulian	cb->s_sid = htons(spx_iss);
152211819Sjulian	spx_iss += SPX_ISSINCR/2;
1523157069Srwatson	SPX_UNLOCK();
152411819Sjulian	cb->s_alo = 1;
152511819Sjulian	cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu;
1526179408Srwatson
1527179408Srwatson	/*
1528179408Srwatson	 * Try to expand fast to full complement of large packets.
1529179408Srwatson	 */
1530157094Srwatson	cb->s_ssthresh = cb->s_cwnd;
153111819Sjulian	cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spx));
1532179408Srwatson
1533179408Srwatson	/*
1534179408Srwatson	 * But allow for lots of little packets as well.
1535179408Srwatson	 */
153611819Sjulian	cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd);
153711819Sjulian}
153811819Sjulian
153911819Sjulian/*
1540157128Srwatson * Close a SPIP control block.  Wake up any sleepers.  We used to free any
1541192754Srwatson * queued packets, but now we defer that until the pcb is discarded.
154211819Sjulian */
1543139931Srwatsonvoid
1544157067Srwatsonspx_close(struct spxpcb *cb)
154511819Sjulian{
154611819Sjulian	struct ipxpcb *ipxp = cb->s_ipxpcb;
154711819Sjulian	struct socket *so = ipxp->ipxp_socket;
154811819Sjulian
1549157094Srwatson	KASSERT(ipxp != NULL, ("spx_close: ipxp == NULL"));
1550139932Srwatson	IPX_LIST_LOCK_ASSERT();
1551139932Srwatson	IPX_LOCK_ASSERT(ipxp);
1552139932Srwatson
1553157128Srwatson	ipxp->ipxp_flags |= IPXP_DROPPED;
155411819Sjulian	soisdisconnected(so);
155511819Sjulian	spxstat.spxs_closed++;
155611819Sjulian}
155725652Sjhay
155811819Sjulian/*
1559157094Srwatson * Someday we may do level 3 handshaking to close a connection or send a
1560157094Srwatson * xerox style error.  For now, just close.  cb will always be invalid after
1561157094Srwatson * this call.
156211819Sjulian */
1563139931Srwatsonstatic void
1564157067Srwatsonspx_usrclosed(struct spxpcb *cb)
156511819Sjulian{
1566139931Srwatson
1567139932Srwatson	IPX_LIST_LOCK_ASSERT();
1568139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1569139932Srwatson
1570139931Srwatson	spx_close(cb);
157111819Sjulian}
157225652Sjhay
1573139931Srwatson/*
1574139931Srwatson * cb will always be invalid after this call.
1575139931Srwatson */
1576139931Srwatsonstatic void
1577157067Srwatsonspx_disconnect(struct spxpcb *cb)
157811819Sjulian{
1579139931Srwatson
1580139932Srwatson	IPX_LIST_LOCK_ASSERT();
1581139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1582139932Srwatson
1583139931Srwatson	spx_close(cb);
158411819Sjulian}
158525652Sjhay
158611819Sjulian/*
1587157094Srwatson * Drop connection, reporting the specified error.  cb will always be invalid
1588157094Srwatson * after this call.
158911819Sjulian */
1590139931Srwatsonstatic void
1591157067Srwatsonspx_drop(struct spxpcb *cb, int errno)
159211819Sjulian{
159311819Sjulian	struct socket *so = cb->s_ipxpcb->ipxp_socket;
159411819Sjulian
1595139932Srwatson	IPX_LIST_LOCK_ASSERT();
1596139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1597139932Srwatson
159811819Sjulian	/*
1599157094Srwatson	 * Someday, in the xerox world we will generate error protocol
1600157094Srwatson	 * packets announcing that the socket has gone away.
160111819Sjulian	 */
160211819Sjulian	if (TCPS_HAVERCVDSYN(cb->s_state)) {
160311819Sjulian		spxstat.spxs_drops++;
160411819Sjulian		cb->s_state = TCPS_CLOSED;
160525652Sjhay		/*tcp_output(cb);*/
160611819Sjulian	} else
160711819Sjulian		spxstat.spxs_conndrops++;
160811819Sjulian	so->so_error = errno;
1609139931Srwatson	spx_close(cb);
161011819Sjulian}
161111819Sjulian
161211819Sjulian/*
1613157094Srwatson * Fast timeout routine for processing delayed acks.
161411819Sjulian */
161511819Sjulianvoid
1616157067Srwatsonspx_fasttimo(void)
161711819Sjulian{
1618139932Srwatson	struct ipxpcb *ipxp;
1619139932Srwatson	struct spxpcb *cb;
162011819Sjulian
1621139932Srwatson	IPX_LIST_LOCK();
1622139444Srwatson	LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) {
1623139932Srwatson		IPX_LOCK(ipxp);
1624157145Srwatson		if (!(ipxp->ipxp_flags & IPXP_SPX) ||
1625157145Srwatson		    (ipxp->ipxp_flags & IPXP_DROPPED)) {
1626157145Srwatson			IPX_UNLOCK(ipxp);
1627157145Srwatson			continue;
162811819Sjulian		}
1629157145Srwatson		cb = ipxtospxpcb(ipxp);
1630157145Srwatson		if (cb->s_flags & SF_DELACK) {
1631157145Srwatson			cb->s_flags &= ~SF_DELACK;
1632157145Srwatson			cb->s_flags |= SF_ACKNOW;
1633157145Srwatson			spxstat.spxs_delack++;
1634157145Srwatson			spx_output(cb, NULL);
1635157145Srwatson		}
1636139932Srwatson		IPX_UNLOCK(ipxp);
1637139444Srwatson	}
1638139932Srwatson	IPX_LIST_UNLOCK();
163911819Sjulian}
164011819Sjulian
164111819Sjulian/*
1642157094Srwatson * spx protocol timeout routine called every 500 ms.  Updates the timers in
1643157094Srwatson * all active pcb's and causes finite state machine actions if timers expire.
164411819Sjulian */
164511819Sjulianvoid
1646157067Srwatsonspx_slowtimo(void)
164711819Sjulian{
1648157128Srwatson	struct ipxpcb *ipxp;
1649139932Srwatson	struct spxpcb *cb;
1650139932Srwatson	int i;
165111819Sjulian
165211819Sjulian	/*
1653157128Srwatson	 * Search through tcb's and update active timers.  Once, timers could
1654157128Srwatson	 * free ipxp's, but now we do that only when detaching a socket.
165511819Sjulian	 */
1656139932Srwatson	IPX_LIST_LOCK();
1657157128Srwatson	LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) {
1658157128Srwatson		IPX_LOCK(ipxp);
1659157145Srwatson		if (!(ipxp->ipxp_flags & IPXP_SPX) ||
1660157145Srwatson		    (ipxp->ipxp_flags & IPXP_DROPPED)) {
1661157128Srwatson			IPX_UNLOCK(ipxp);
1662139444Srwatson			continue;
1663157128Srwatson		}
1664157128Srwatson
1665157128Srwatson		cb = (struct spxpcb *)ipxp->ipxp_pcb;
1666157128Srwatson		KASSERT(cb != NULL, ("spx_slowtimo: cb == NULL"));
166711819Sjulian		for (i = 0; i < SPXT_NTIMERS; i++) {
166811819Sjulian			if (cb->s_timer[i] && --cb->s_timer[i] == 0) {
1669157128Srwatson				spx_timers(cb, i);
1670157128Srwatson				if (ipxp->ipxp_flags & IPXP_DROPPED)
1671139581Srwatson					break;
167211819Sjulian			}
167311819Sjulian		}
1674157128Srwatson		if (!(ipxp->ipxp_flags & IPXP_DROPPED)) {
1675139581Srwatson			cb->s_idle++;
1676139581Srwatson			if (cb->s_rtt)
1677139581Srwatson				cb->s_rtt++;
1678139581Srwatson		}
1679157128Srwatson		IPX_UNLOCK(ipxp);
168011819Sjulian	}
1681157069Srwatson	IPX_LIST_UNLOCK();
1682157069Srwatson	SPX_LOCK();
168311819Sjulian	spx_iss += SPX_ISSINCR/PR_SLOWHZ;		/* increment iss */
1684157069Srwatson	SPX_UNLOCK();
168511819Sjulian}
168625652Sjhay
168711819Sjulian/*
168811819Sjulian * SPX timer processing.
168911819Sjulian */
1690157128Srwatsonstatic void
1691157067Srwatsonspx_timers(struct spxpcb *cb, int timer)
169211819Sjulian{
169311819Sjulian	long rexmt;
169411819Sjulian	int win;
169511819Sjulian
1696139932Srwatson	IPX_LIST_LOCK_ASSERT();
1697139932Srwatson	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1698139932Srwatson
169911819Sjulian	cb->s_force = 1 + timer;
170011819Sjulian	switch (timer) {
170111819Sjulian	case SPXT_2MSL:
1702157124Srwatson		/*
1703157124Srwatson		 * 2 MSL timeout in shutdown went off.  TCP deletes
1704157124Srwatson		 * connection control block.
1705157124Srwatson		 */
170611819Sjulian		printf("spx: SPXT_2MSL went off for no reason\n");
170711819Sjulian		cb->s_timer[timer] = 0;
170811819Sjulian		break;
170911819Sjulian
171011819Sjulian	case SPXT_REXMT:
1711157124Srwatson		/*
1712157124Srwatson		 * Retransmission timer went off.  Message has not been acked
1713157124Srwatson		 * within retransmit interval.  Back off to a longer
1714157124Srwatson		 * retransmit interval and retransmit one packet.
1715157124Srwatson		 */
171611819Sjulian		if (++cb->s_rxtshift > SPX_MAXRXTSHIFT) {
171711819Sjulian			cb->s_rxtshift = SPX_MAXRXTSHIFT;
171811819Sjulian			spxstat.spxs_timeoutdrop++;
1719139931Srwatson			spx_drop(cb, ETIMEDOUT);
172011819Sjulian			break;
172111819Sjulian		}
172211819Sjulian		spxstat.spxs_rexmttimeo++;
172311819Sjulian		rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
172411819Sjulian		rexmt *= spx_backoff[cb->s_rxtshift];
172511819Sjulian		SPXT_RANGESET(cb->s_rxtcur, rexmt, SPXTV_MIN, SPXTV_REXMTMAX);
172611819Sjulian		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1727157094Srwatson
172811819Sjulian		/*
1729157094Srwatson		 * If we have backed off fairly far, our srtt estimate is
1730157094Srwatson		 * probably bogus.  Clobber it so we'll take the next rtt
1731157094Srwatson		 * measurement as our srtt; move the current srtt into rttvar
1732157094Srwatson		 * to keep the current retransmit times until then.
173311819Sjulian		 */
173411819Sjulian		if (cb->s_rxtshift > SPX_MAXRXTSHIFT / 4 ) {
173511819Sjulian			cb->s_rttvar += (cb->s_srtt >> 2);
173611819Sjulian			cb->s_srtt = 0;
173711819Sjulian		}
173811819Sjulian		cb->s_snxt = cb->s_rack;
1739157094Srwatson
174011819Sjulian		/*
174111819Sjulian		 * If timing a packet, stop the timer.
174211819Sjulian		 */
174311819Sjulian		cb->s_rtt = 0;
1744157094Srwatson
174511819Sjulian		/*
174611819Sjulian		 * See very long discussion in tcp_timer.c about congestion
1747157094Srwatson		 * window and sstrhesh.
174811819Sjulian		 */
174911819Sjulian		win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2;
175011819Sjulian		if (win < 2)
175111819Sjulian			win = 2;
175211819Sjulian		cb->s_cwnd = CUNIT;
175311819Sjulian		cb->s_ssthresh = win * CUNIT;
1754139579Srwatson		spx_output(cb, NULL);
175511819Sjulian		break;
175611819Sjulian
175711819Sjulian	case SPXT_PERSIST:
1758157094Srwatson		/*
1759157094Srwatson		 * Persistance timer into zero window.  Force a probe to be
1760157094Srwatson		 * sent.
1761157094Srwatson		 */
176211819Sjulian		spxstat.spxs_persisttimeo++;
176311819Sjulian		spx_setpersist(cb);
1764139579Srwatson		spx_output(cb, NULL);
176511819Sjulian		break;
176611819Sjulian
176711819Sjulian	case SPXT_KEEP:
1768157094Srwatson		/*
1769157094Srwatson		 * Keep-alive timer went off; send something or drop
1770157094Srwatson		 * connection if idle for too long.
1771157094Srwatson		 */
177211819Sjulian		spxstat.spxs_keeptimeo++;
177311819Sjulian		if (cb->s_state < TCPS_ESTABLISHED)
177411819Sjulian			goto dropit;
177511819Sjulian		if (cb->s_ipxpcb->ipxp_socket->so_options & SO_KEEPALIVE) {
177611819Sjulian		    	if (cb->s_idle >= SPXTV_MAXIDLE)
177711819Sjulian				goto dropit;
177811819Sjulian			spxstat.spxs_keepprobe++;
1779139579Srwatson			spx_output(cb, NULL);
178097658Stanimura		} else
178111819Sjulian			cb->s_idle = 0;
178211819Sjulian		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
178311819Sjulian		break;
1784157094Srwatson
178511819Sjulian	dropit:
178611819Sjulian		spxstat.spxs_keepdrops++;
1787139931Srwatson		spx_drop(cb, ETIMEDOUT);
178811819Sjulian		break;
1789157124Srwatson
1790157124Srwatson	default:
1791157124Srwatson		panic("spx_timers: unknown timer %d", timer);
179211819Sjulian	}
179311819Sjulian}
1794