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