spx_reass.c revision 139823
19313Ssos/*-
2230132Suqs * Copyright (c) 2004 Robert N. M. Watson
39313Ssos * Copyright (c) 1995, Mike Mitchell
49313Ssos * Copyright (c) 1984, 1985, 1986, 1987, 1993
59313Ssos *	The Regents of the University of California.  All rights reserved.
69313Ssos *
79313Ssos * Redistribution and use in source and binary forms, with or without
89313Ssos * modification, are permitted provided that the following conditions
9111798Sdes * are met:
109313Ssos * 1. Redistributions of source code must retain the above copyright
119313Ssos *    notice, this list of conditions and the following disclaimer.
129313Ssos * 2. Redistributions in binary form must reproduce the above copyright
139313Ssos *    notice, this list of conditions and the following disclaimer in the
149313Ssos *    documentation and/or other materials provided with the distribution.
1597748Sschweikh * 3. All advertising materials mentioning features or use of this software
169313Ssos *    must display the following acknowledgement:
179313Ssos *	This product includes software developed by the University of
189313Ssos *	California, Berkeley and its contributors.
199313Ssos * 4. Neither the name of the University nor the names of its contributors
209313Ssos *    may be used to endorse or promote products derived from this software
219313Ssos *    without specific prior written permission.
229313Ssos *
239313Ssos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
249313Ssos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
259313Ssos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
269313Ssos * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
279313Ssos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
289313Ssos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29116173Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30116173Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31116173Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32156874Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3331784Seivind * SUCH DAMAGE.
349313Ssos *
359313Ssos *	@(#)spx_usrreq.h
36224778Srwatson */
3776166Smarkm
3876166Smarkm#include <sys/cdefs.h>
399313Ssos__FBSDID("$FreeBSD: head/sys/netipx/spx_usrreq.c 139823 2005-01-07 01:45:51Z imp $");
409313Ssos
419313Ssos#include <sys/param.h>
4231561Sbde#include <sys/lock.h>
439313Ssos#include <sys/malloc.h>
4472538Sjlemon#include <sys/mbuf.h>
4576166Smarkm#include <sys/mutex.h>
46168014Sjulian#include <sys/proc.h>
4776166Smarkm#include <sys/protosw.h>
48162201Snetchild#include <sys/signalvar.h>
49166085Skib#include <sys/socket.h>
50102814Siedowse#include <sys/socketvar.h>
5176166Smarkm#include <sys/sx.h>
5214331Speter#include <sys/systm.h>
53162585Snetchild
5476166Smarkm#include <net/route.h>
5512458Sbde#include <netinet/tcp_fsm.h>
56163606Srwatson
57163606Srwatson#include <netipx/ipx.h>
5872538Sjlemon#include <netipx/ipx_pcb.h>
5972538Sjlemon#include <netipx/ipx_var.h>
6072538Sjlemon#include <netipx/spx.h>
6172538Sjlemon#include <netipx/spx_debug.h>
62140214Sobrien#include <netipx/spx_timer.h>
63140214Sobrien#include <netipx/spx_var.h>
64140214Sobrien
65140214Sobrien/*
6664905Smarcel * SPX protocol implementation.
6768583Smarcel */
68133816Stjrstatic u_short 	spx_iss;
69246085Sjhbstatic u_short	spx_newchecks[50];
7064905Smarcelstatic int	spx_hardnosed;
71177997Skibstatic int	spx_use_delack = 0;
729313Ssosstatic int	traceallspxs = 0;
739313Ssosstatic struct	spx 	spx_savesi;
7483366Sjulianstatic struct	spx_istat spx_istat;
759313Ssos
76102814Siedowse/* Following was struct spxstat spxstat; */
77102814Siedowse#ifndef spxstat
789313Ssos#define spxstat spx_istat.newstats
79102814Siedowse#endif
8014331Speter
819313Ssosstatic const int spx_backoff[SPX_MAXRXTSHIFT+1] =
8272543Sjlemon    { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
83102814Siedowse
849313Ssosstatic	struct spxpcb *spx_close(struct spxpcb *cb);
85102814Siedowsestatic	struct spxpcb *spx_disconnect(struct spxpcb *cb);
86102814Siedowsestatic	struct spxpcb *spx_drop(struct spxpcb *cb, int errno);
87102814Siedowsestatic	int spx_output(struct spxpcb *cb, struct mbuf *m0);
88102814Siedowsestatic	int spx_reass(struct spxpcb *cb, struct spx *si);
899313Ssosstatic	void spx_setpersist(struct spxpcb *cb);
909313Ssosstatic	void spx_template(struct spxpcb *cb);
91168014Sjulianstatic	struct spxpcb *spx_timers(struct spxpcb *cb, int timer);
92168014Sjulianstatic	struct spxpcb *spx_usrclosed(struct spxpcb *cb);
93177997Skib
949313Ssosstatic	int spx_usr_abort(struct socket *so);
95255219Spjdstatic	int spx_accept(struct socket *so, struct sockaddr **nam);
9683382Sjhbstatic	int spx_attach(struct socket *so, int proto, struct thread *td);
97166085Skibstatic	int spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td);
98166085Skibstatic	int spx_connect(struct socket *so, struct sockaddr *nam,
99102814Siedowse			struct thread *td);
10014331Speterstatic	int spx_detach(struct socket *so);
101102814Siedowsestatic	int spx_usr_disconnect(struct socket *so);
102168014Sjulianstatic	int spx_listen(struct socket *so, struct thread *td);
103168014Sjulianstatic	int spx_rcvd(struct socket *so, int flags);
104102814Siedowsestatic	int spx_rcvoob(struct socket *so, struct mbuf *m, int flags);
105168014Sjulianstatic	int spx_send(struct socket *so, int flags, struct mbuf *m,
106168014Sjulian		     struct sockaddr *addr, struct mbuf *control,
107102814Siedowse		     struct thread *td);
108168014Sjulianstatic	int spx_shutdown(struct socket *so);
109168014Sjulianstatic	int spx_sp_attach(struct socket *so, int proto, struct thread *td);
110168014Sjulian
111168014Sjulianstruct	pr_usrreqs spx_usrreqs = {
112168014Sjulian	.pru_abort =		spx_usr_abort,
113102814Siedowse	.pru_accept =		spx_accept,
114168014Sjulian	.pru_attach =		spx_attach,
115102814Siedowse	.pru_bind =		spx_bind,
116168014Sjulian	.pru_connect =		spx_connect,
117102814Siedowse	.pru_control =		ipx_control,
118168014Sjulian	.pru_detach =		spx_detach,
119102814Siedowse	.pru_disconnect =	spx_usr_disconnect,
120168014Sjulian	.pru_listen =		spx_listen,
121102814Siedowse	.pru_peeraddr =		ipx_peeraddr,
122168014Sjulian	.pru_rcvd =		spx_rcvd,
123102814Siedowse	.pru_rcvoob =		spx_rcvoob,
124168014Sjulian	.pru_send =		spx_send,
125102814Siedowse	.pru_shutdown =		spx_shutdown,
126168014Sjulian	.pru_sockaddr =		ipx_sockaddr,
127102814Siedowse};
128168014Sjulian
129102814Siedowsestruct	pr_usrreqs spx_usrreq_sps = {
130168014Sjulian	.pru_abort =		spx_usr_abort,
131166085Skib	.pru_accept =		spx_accept,
132168014Sjulian	.pru_attach =		spx_sp_attach,
133166085Skib	.pru_bind =		spx_bind,
134205423Sed	.pru_connect =		spx_connect,
135205423Sed	.pru_control =		ipx_control,
136166085Skib	.pru_detach =		spx_detach,
1379313Ssos	.pru_disconnect =	spx_usr_disconnect,
138178036Srdivacky	.pru_listen =		spx_listen,
139178036Srdivacky	.pru_peeraddr =		ipx_peeraddr,
140166085Skib	.pru_rcvd =		spx_rcvd,
141166085Skib	.pru_rcvoob =		spx_rcvoob,
142166085Skib	.pru_send =		spx_send,
143166085Skib	.pru_shutdown =		spx_shutdown,
144166085Skib	.pru_sockaddr =		ipx_sockaddr,
145166085Skib};
146166085Skib
147255219Spjdvoid
148166085Skibspx_init()
149166085Skib{
150166085Skib
151166085Skib	spx_iss = 1; /* WRONG !! should fish it out of TODR */
152166085Skib}
153166085Skib
154166085Skibvoid
155247602Spjdspx_input(m, ipxp)
156166085Skib	register struct mbuf *m;
157166085Skib	register struct ipxpcb *ipxp;
158166085Skib{
159166085Skib	register struct spxpcb *cb;
160166085Skib	register struct spx *si = mtod(m, struct spx *);
161166085Skib	register struct socket *so;
162166085Skib	int dropsocket = 0;
163166085Skib	short ostate = 0;
164166085Skib
165166085Skib	spxstat.spxs_rcvtotal++;
166166085Skib	KASSERT(ipxp != NULL, ("spx_input: NULL ipxpcb"));
167166085Skib
168166085Skib	cb = ipxtospxpcb(ipxp);
169166085Skib	if (cb == NULL)
170166085Skib		goto bad;
1719313Ssos
17214331Speter	if (m->m_len < sizeof(*si)) {
173166085Skib		if ((m = m_pullup(m, sizeof(*si))) == NULL) {
174166085Skib			spxstat.spxs_rcvshort++;
17514331Speter			return;
176177997Skib		}
177177997Skib		si = mtod(m, struct spx *);
1789313Ssos	}
1799313Ssos	si->si_seq = ntohs(si->si_seq);
1809313Ssos	si->si_ack = ntohs(si->si_ack);
181168014Sjulian	si->si_alo = ntohs(si->si_alo);
182168014Sjulian
183177997Skib	so = ipxp->ipxp_socket;
184177997Skib
185168014Sjulian	if (so->so_options & SO_DEBUG || traceallspxs) {
186177997Skib		ostate = cb->s_state;
187177997Skib		spx_savesi = *si;
188177997Skib	}
189177997Skib	if (so->so_options & SO_ACCEPTCONN) {
190177997Skib		struct spxpcb *ocb = cb;
191168014Sjulian
192168014Sjulian		so = sonewconn(so, 0);
193168014Sjulian		if (so == NULL) {
194177997Skib			goto drop;
195168014Sjulian		}
196177997Skib		/*
197168014Sjulian		 * This is ugly, but ....
198168014Sjulian		 *
199168014Sjulian		 * Mark socket as temporary until we're
200168014Sjulian		 * committed to keeping it.  The code at
201168014Sjulian		 * ``drop'' and ``dropwithreset'' check the
202168014Sjulian		 * flag dropsocket to see if the temporary
203168014Sjulian		 * socket created here should be discarded.
204168014Sjulian		 * We mark the socket as discardable until
205168014Sjulian		 * we're committed to it below in TCPS_LISTEN.
206168014Sjulian		 */
207168014Sjulian		dropsocket++;
208168014Sjulian		ipxp = (struct ipxpcb *)so->so_pcb;
209168014Sjulian		ipxp->ipxp_laddr = si->si_dna;
210168014Sjulian		cb = ipxtospxpcb(ipxp);
211168014Sjulian		cb->s_mtu = ocb->s_mtu;		/* preserve sockopts */
212168014Sjulian		cb->s_flags = ocb->s_flags;	/* preserve sockopts */
213168014Sjulian		cb->s_flags2 = ocb->s_flags2;	/* preserve sockopts */
214168014Sjulian		cb->s_state = TCPS_LISTEN;
215178036Srdivacky	}
216168014Sjulian
217168014Sjulian	/*
218168014Sjulian	 * Packet received on connection.
21983366Sjulian	 * reset idle time and keep-alive timer;
2209313Ssos	 */
2219313Ssos	cb->s_idle = 0;
22212858Speter	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
22312858Speter
2249313Ssos	switch (cb->s_state) {
22512858Speter
2269313Ssos	case TCPS_LISTEN:{
22712858Speter		struct sockaddr_ipx *sipx, ssipx;
2289313Ssos		struct ipx_addr laddr;
2299313Ssos
2309313Ssos		/*
23172543Sjlemon		 * If somebody here was carying on a conversation
23272543Sjlemon		 * and went away, and his pen pal thinks he can
23383221Smarcel		 * still talk, we get the misdirected packet.
2349313Ssos		 */
23512858Speter		if (spx_hardnosed && (si->si_did != 0 || si->si_seq != 0)) {
23612858Speter			spx_istat.gonawy++;
2379313Ssos			goto dropwithreset;
238225617Skmacy		}
2399313Ssos		sipx = &ssipx;
2409313Ssos		bzero(sipx, sizeof *sipx);
2419313Ssos		sipx->sipx_len = sizeof(*sipx);
24214331Speter		sipx->sipx_family = AF_IPX;
24383366Sjulian		sipx->sipx_addr = si->si_sna;
24414331Speter		laddr = ipxp->ipxp_laddr;
24514331Speter		if (ipx_nullhost(laddr))
24614331Speter			ipxp->ipxp_laddr = si->si_dna;
24714331Speter		if (ipx_pcbconnect(ipxp, (struct sockaddr *)sipx, &thread0)) {
24814331Speter			ipxp->ipxp_laddr = laddr;
24914331Speter			spx_istat.noconn++;
25072543Sjlemon			goto drop;
25172543Sjlemon		}
25272543Sjlemon		spx_template(cb);
25314331Speter		dropsocket = 0;		/* committed to socket */
25414331Speter		cb->s_did = si->si_sid;
25514331Speter		cb->s_rack = si->si_ack;
25614331Speter		cb->s_ralo = si->si_alo;
25714331Speter#define THREEWAYSHAKE
25814331Speter#ifdef THREEWAYSHAKE
25914331Speter		cb->s_state = TCPS_SYN_RECEIVED;
260225617Skmacy		cb->s_force = 1 + SPXT_KEEP;
26114331Speter		spxstat.spxs_accepts++;
26214331Speter		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
263111797Sdes		}
26414331Speter		break;
26514331Speter	/*
26683366Sjulian	 * This state means that we have heard a response
26714331Speter	 * to our acceptance of their connection
26814331Speter	 * It is probably logically unnecessary in this
26914331Speter	 * implementation.
2709313Ssos	 */
27183366Sjulian	 case TCPS_SYN_RECEIVED: {
2729313Ssos		if (si->si_did != cb->s_sid) {
27314331Speter			spx_istat.wrncon++;
27414331Speter			goto drop;
27514331Speter		}
27614331Speter#endif
27714331Speter		ipxp->ipxp_fport =  si->si_sport;
27883366Sjulian		cb->s_timer[SPXT_REXMT] = 0;
27914331Speter		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
28014331Speter		soisconnected(so);
28183221Smarcel		cb->s_state = TCPS_ESTABLISHED;
28283221Smarcel		spxstat.spxs_accepts++;
28383221Smarcel		}
28483221Smarcel		break;
28583221Smarcel
28683221Smarcel	/*
28783221Smarcel	 * This state means that we have gotten a response
28883221Smarcel	 * to our attempt to establish a connection.
28983221Smarcel	 * We fill in the data from the other side,
29083221Smarcel	 * telling us which port to respond to, instead of the well-
291179651Srdivacky	 * known one we might have sent to in the first place.
29283221Smarcel	 * We also require that this is a response to our
29383221Smarcel	 * connection id.
29483221Smarcel	 */
29583221Smarcel	case TCPS_SYN_SENT:
29683221Smarcel		if (si->si_did != cb->s_sid) {
29783221Smarcel			spx_istat.notme++;
29883221Smarcel			goto drop;
29983221Smarcel		}
30083221Smarcel		spxstat.spxs_connects++;
30183221Smarcel		cb->s_did = si->si_sid;
30283221Smarcel		cb->s_rack = si->si_ack;
30383221Smarcel		cb->s_ralo = si->si_alo;
30483221Smarcel		cb->s_dport = ipxp->ipxp_fport =  si->si_sport;
305182892Srdivacky		cb->s_timer[SPXT_REXMT] = 0;
306182892Srdivacky		cb->s_flags |= SF_ACKNOW;
307182892Srdivacky		soisconnected(so);
308182892Srdivacky		cb->s_state = TCPS_ESTABLISHED;
309182892Srdivacky		/* Use roundtrip time of connection request for initial rtt */
310182892Srdivacky		if (cb->s_rtt) {
311182892Srdivacky			cb->s_srtt = cb->s_rtt << 3;
31283221Smarcel			cb->s_rttvar = cb->s_rtt << 1;
313182892Srdivacky			SPXT_RANGESET(cb->s_rxtcur,
314182892Srdivacky			    ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
315182892Srdivacky			    SPXTV_MIN, SPXTV_REXMTMAX);
316182892Srdivacky			    cb->s_rtt = 0;
317182892Srdivacky		}
318182892Srdivacky	}
31983221Smarcel	if (so->so_options & SO_DEBUG || traceallspxs)
32083221Smarcel		spx_trace(SA_INPUT, (u_char)ostate, cb, &spx_savesi, 0);
32183221Smarcel
32283366Sjulian	m->m_len -= sizeof(struct ipx);
32383221Smarcel	m->m_pkthdr.len -= sizeof(struct ipx);
32414331Speter	m->m_data += sizeof(struct ipx);
325111798Sdes
32683221Smarcel	if (spx_reass(cb, si)) {
32783221Smarcel		m_freem(m);
32883221Smarcel	}
32983221Smarcel	if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT)))
33083221Smarcel		spx_output(cb, NULL);
331182892Srdivacky	cb->s_flags &= ~(SF_WIN|SF_RXT);
332255219Spjd	return;
33383221Smarcel
33483221Smarceldropwithreset:
33583221Smarcel	if (dropsocket) {
33683221Smarcel		struct socket *head;
337182892Srdivacky		ACCEPT_LOCK();
338182892Srdivacky		KASSERT((so->so_qstate & SQ_INCOMP) != 0,
33983221Smarcel		    ("spx_input: nascent socket not SQ_INCOMP on soabort()"));
34083221Smarcel		head = so->so_head;
341241896Skib		TAILQ_REMOVE(&head->so_incomp, so, so_list);
3429313Ssos		head->so_incqlen--;
343160276Sjhb		so->so_qstate &= ~SQ_INCOMP;
344160276Sjhb		so->so_head = NULL;
345160276Sjhb		ACCEPT_UNLOCK();
346160276Sjhb		soabort(so);
347160276Sjhb	}
348188572Snetchild	si->si_seq = ntohs(si->si_seq);
349160276Sjhb	si->si_ack = ntohs(si->si_ack);
350160276Sjhb	si->si_alo = ntohs(si->si_alo);
351160276Sjhb	m_freem(dtom(si));
352160276Sjhb	if (cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || traceallspxs)
353255219Spjd		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
354255219Spjd	return;
355255219Spjd
35683221Smarceldrop:
3579313Ssosbad:
35889306Salfred	if (cb == NULL || cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG ||
35989306Salfred            traceallspxs)
36083221Smarcel		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
36189306Salfred	m_freem(m);
3629313Ssos}
363238029Skib
364116678Sphkstatic int spxrexmtthresh = 3;
36589306Salfred
366238029Skib/*
36789306Salfred * This is structurally similar to the tcp reassembly routine
36883221Smarcel * but its function is somewhat different:  It merely queues
36989306Salfred * packets up, and suppresses duplicates.
3709313Ssos */
3719313Ssosstatic int
37283221Smarcelspx_reass(cb, si)
37383221Smarcelregister struct spxpcb *cb;
374111119Simpregister struct spx *si;
375182892Srdivacky{
376188588Sjhb	register struct spx_q *q;
37783221Smarcel	register struct mbuf *m;
37883221Smarcel	register struct socket *so = cb->s_ipxpcb->ipxp_socket;
37983221Smarcel	char packetp = cb->s_flags & SF_HI;
38083221Smarcel	int incr;
38183221Smarcel	char wakeup = 0;
38283221Smarcel
38383221Smarcel	if (si == SI(0))
38483366Sjulian		goto present;
38583221Smarcel	/*
38683221Smarcel	 * Update our news from them.
3879313Ssos	 */
388101189Srwatson	if (si->si_cc & SPX_SA)
389101189Srwatson		cb->s_flags |= (spx_use_delack ? SF_DELACK : SF_ACKNOW);
390101189Srwatson	if (SSEQ_GT(si->si_alo, cb->s_ralo))
391101189Srwatson		cb->s_flags |= SF_WIN;
392172930Srwatson	if (SSEQ_LEQ(si->si_ack, cb->s_rack)) {
393101189Srwatson		if ((si->si_cc & SPX_SP) && cb->s_rack != (cb->s_smax + 1)) {
394101189Srwatson			spxstat.spxs_rcvdupack++;
39583221Smarcel			/*
39683221Smarcel			 * If this is a completely duplicate ack
39783221Smarcel			 * and other conditions hold, we assume
3989313Ssos			 * a packet has been dropped and retransmit
39983221Smarcel			 * it exactly as in tcp_input().
40083221Smarcel			 */
40183221Smarcel			if (si->si_ack != cb->s_rack ||
40283221Smarcel			    si->si_alo != cb->s_ralo)
40383221Smarcel				cb->s_dupacks = 0;
4049313Ssos			else if (++cb->s_dupacks == spxrexmtthresh) {
40583221Smarcel				u_short onxt = cb->s_snxt;
40624654Sdfr				int cwnd = cb->s_cwnd;
40783221Smarcel
40883221Smarcel				cb->s_snxt = si->si_ack;
40983221Smarcel				cb->s_cwnd = CUNIT;
41083221Smarcel				cb->s_force = 1 + SPXT_REXMT;
41183221Smarcel				spx_output(cb, NULL);
41283221Smarcel				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
41383221Smarcel				cb->s_rtt = 0;
41483221Smarcel				if (cwnd >= 4 * CUNIT)
41583221Smarcel					cb->s_cwnd = cwnd / 2;
41683221Smarcel				if (SSEQ_GT(onxt, cb->s_snxt))
41783221Smarcel					cb->s_snxt = onxt;
41883221Smarcel				return (1);
41983221Smarcel			}
42083221Smarcel		} else
42183221Smarcel			cb->s_dupacks = 0;
42224654Sdfr		goto update_window;
42324654Sdfr	}
42483221Smarcel	cb->s_dupacks = 0;
42583221Smarcel	/*
42683221Smarcel	 * If our correspondent acknowledges data we haven't sent
42783221Smarcel	 * TCP would drop the packet after acking.  We'll be a little
42883221Smarcel	 * more permissive
42983221Smarcel	 */
43083221Smarcel	if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) {
43183221Smarcel		spxstat.spxs_rcvacktoomuch++;
43283221Smarcel		si->si_ack = cb->s_smax + 1;
43383221Smarcel	}
43483221Smarcel	spxstat.spxs_rcvackpack++;
43583221Smarcel	/*
43683221Smarcel	 * If transmit timer is running and timed sequence
43783221Smarcel	 * number was acked, update smoothed round trip time.
43883221Smarcel	 * See discussion of algorithm in tcp_input.c
43983221Smarcel	 */
44083221Smarcel	if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) {
44183221Smarcel		spxstat.spxs_rttupdated++;
44283221Smarcel		if (cb->s_srtt != 0) {
44383221Smarcel			register short delta;
44483221Smarcel			delta = cb->s_rtt - (cb->s_srtt >> 3);
44583221Smarcel			if ((cb->s_srtt += delta) <= 0)
44683221Smarcel				cb->s_srtt = 1;
447182892Srdivacky			if (delta < 0)
448182892Srdivacky				delta = -delta;
44983221Smarcel			delta -= (cb->s_rttvar >> 2);
45083221Smarcel			if ((cb->s_rttvar += delta) <= 0)
45183221Smarcel				cb->s_rttvar = 1;
45283221Smarcel		} else {
45383221Smarcel			/*
45483221Smarcel			 * No rtt measurement yet
45583221Smarcel			 */
45683221Smarcel			cb->s_srtt = cb->s_rtt << 3;
457182892Srdivacky			cb->s_rttvar = cb->s_rtt << 1;
458182892Srdivacky		}
459182892Srdivacky		cb->s_rtt = 0;
460182892Srdivacky		cb->s_rxtshift = 0;
461182892Srdivacky		SPXT_RANGESET(cb->s_rxtcur,
462182892Srdivacky			((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
463182892Srdivacky			SPXTV_MIN, SPXTV_REXMTMAX);
46483221Smarcel	}
465182892Srdivacky	/*
466182892Srdivacky	 * If all outstanding data is acked, stop retransmit
467182892Srdivacky	 * timer and remember to restart (more output or persist).
468182892Srdivacky	 * If there is more data to be acked, restart retransmit
469182892Srdivacky	 * timer, using current (possibly backed-off) value;
470182892Srdivacky	 */
471182892Srdivacky	if (si->si_ack == cb->s_smax + 1) {
472182892Srdivacky		cb->s_timer[SPXT_REXMT] = 0;
473182892Srdivacky		cb->s_flags |= SF_RXT;
474182892Srdivacky	} else if (cb->s_timer[SPXT_PERSIST] == 0)
475182892Srdivacky		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
476182892Srdivacky	/*
477182892Srdivacky	 * When new data is acked, open the congestion window.
478182892Srdivacky	 * If the window gives us less than ssthresh packets
479182892Srdivacky	 * in flight, open exponentially (maxseg at a time).
480182892Srdivacky	 * Otherwise open linearly (maxseg^2 / cwnd at a time).
481182892Srdivacky	 */
482182892Srdivacky	incr = CUNIT;
483182892Srdivacky	if (cb->s_cwnd > cb->s_ssthresh)
484182892Srdivacky		incr = max(incr * incr / cb->s_cwnd, 1);
485182892Srdivacky	cb->s_cwnd = min(cb->s_cwnd + incr, cb->s_cwmx);
486182892Srdivacky	/*
487182892Srdivacky	 * Trim Acked data from output queue.
488182892Srdivacky	 */
489182892Srdivacky	SOCKBUF_LOCK(&so->so_snd);
490182892Srdivacky	while ((m = so->so_snd.sb_mb) != NULL) {
491182892Srdivacky		if (SSEQ_LT((mtod(m, struct spx *))->si_seq, si->si_ack))
49283221Smarcel			sbdroprecord_locked(&so->so_snd);
49383221Smarcel		else
49483221Smarcel			break;
49583221Smarcel	}
49683221Smarcel	sowwakeup_locked(so);
49783221Smarcel	cb->s_rack = si->si_ack;
49883221Smarcelupdate_window:
49983221Smarcel	if (SSEQ_LT(cb->s_snxt, cb->s_rack))
50083221Smarcel		cb->s_snxt = cb->s_rack;
50183221Smarcel	if (SSEQ_LT(cb->s_swl1, si->si_seq) || ((cb->s_swl1 == si->si_seq &&
50283221Smarcel	    (SSEQ_LT(cb->s_swl2, si->si_ack))) ||
50383221Smarcel	     (cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo)))) {
50483221Smarcel		/* keep track of pure window updates */
50583221Smarcel		if ((si->si_cc & SPX_SP) && cb->s_swl2 == si->si_ack
50683221Smarcel		    && SSEQ_LT(cb->s_ralo, si->si_alo)) {
50710355Sswallace			spxstat.spxs_rcvwinupd++;
5089313Ssos			spxstat.spxs_rcvdupack--;
509217578Skib		}
510217578Skib		cb->s_ralo = si->si_alo;
511217578Skib		cb->s_swl1 = si->si_seq;
512217578Skib		cb->s_swl2 = si->si_ack;
5139313Ssos		cb->s_swnd = (1 + si->si_alo - si->si_ack);
51483221Smarcel		if (cb->s_swnd > cb->s_smxw)
51583221Smarcel			cb->s_smxw = cb->s_swnd;
51610355Sswallace		cb->s_flags |= SF_WIN;
5179313Ssos	}
51883366Sjulian	/*
51983221Smarcel	 * If this packet number is higher than that which
5209313Ssos	 * we have allocated refuse it, unless urgent
521247764Seadler	 */
52283221Smarcel	if (SSEQ_GT(si->si_seq, cb->s_alo)) {
523175294Sattilio		if (si->si_cc & SPX_SP) {
524238029Skib			spxstat.spxs_rcvwinprobe++;
52589306Salfred			return (1);
52683221Smarcel		} else
527182892Srdivacky			spxstat.spxs_rcvpackafterwin++;
52883221Smarcel		if (si->si_cc & SPX_OB) {
5299313Ssos			if (SSEQ_GT(si->si_seq, cb->s_alo + 60)) {
53014331Speter				m_freem(dtom(si));
53183221Smarcel				return (0);
53283366Sjulian			} /* else queue this packet; */
53383221Smarcel		} else {
53483221Smarcel#ifdef BROKEN
53583221Smarcel			/*
53683221Smarcel			 * XXXRW: This is broken on at least one count:
53783221Smarcel			 * spx_close() will free the ipxp and related parts,
53883221Smarcel			 * which are then touched by spx_input() after the
53983221Smarcel			 * return from spx_reass().
54083366Sjulian			 */
54183221Smarcel			/*register struct socket *so = cb->s_ipxpcb->ipxp_socket;
54283221Smarcel			if (so->so_state && SS_NOFDREF) {
54383221Smarcel				spx_close(cb);
54483366Sjulian			} else
54583221Smarcel				       would crash system*/
54683221Smarcel#endif
54783221Smarcel			spx_istat.notyet++;
54883221Smarcel			m_freem(dtom(si));
54983221Smarcel			return (0);
55083221Smarcel		}
55183221Smarcel	}
55283366Sjulian	/*
55383221Smarcel	 * If this is a system packet, we don't need to
55483221Smarcel	 * queue it up, and won't update acknowledge #
55514331Speter	 */
55614331Speter	if (si->si_cc & SPX_SP) {
55714331Speter		return (1);
55814331Speter	}
55914331Speter	/*
56083366Sjulian	 * We have already seen this packet, so drop.
56114331Speter	 */
562102814Siedowse	if (SSEQ_LT(si->si_seq, cb->s_ack)) {
563102814Siedowse		spx_istat.bdreas++;
56414331Speter		spxstat.spxs_rcvduppack++;
565162585Snetchild		if (si->si_seq == cb->s_ack - 1)
566227691Sed			spx_istat.lstdup++;
567162585Snetchild		return (1);
568162585Snetchild	}
569102814Siedowse	/*
57014331Speter	 * Loop through all packets queued up to insert in
57114331Speter	 * appropriate sequence.
57272543Sjlemon	 */
573227691Sed	for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) {
57414331Speter		if (si->si_seq == SI(q)->si_seq) {
575227691Sed			spxstat.spxs_rcvduppack++;
576102814Siedowse			return (1);
577162585Snetchild		}
578102814Siedowse		if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) {
57914331Speter			spxstat.spxs_rcvoopack++;
58014331Speter			break;
58114331Speter		}
582177997Skib	}
583177997Skib	insque(si, q->si_prev);
584177997Skib	/*
585227693Sed	 * If this packet is urgent, inform process
586177997Skib	 */
587227693Sed	if (si->si_cc & SPX_OB) {
588227693Sed		cb->s_iobc = ((char *)si)[1 + sizeof(*si)];
589177997Skib		sohasoutofband(so);
590227691Sed		cb->s_oobflags |= SF_IOOB;
591177997Skib	}
592177997Skibpresent:
593177997Skib#define SPINC sizeof(struct spxhdr)
594177997Skib	SOCKBUF_LOCK(&so->so_rcv);
595177997Skib	/*
596177997Skib	 * Loop through all packets queued up to update acknowledge
597177997Skib	 * number, and present all acknowledged data to user;
598227691Sed	 * If in packet interface mode, show packet headers.
599177997Skib	 */
600177997Skib	for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) {
601227693Sed		  if (SI(q)->si_seq == cb->s_ack) {
602227693Sed			cb->s_ack++;
603177997Skib			m = dtom(q);
604177997Skib			if (SI(q)->si_cc & SPX_OB) {
605177997Skib				cb->s_oobflags &= ~SF_IOOB;
606177997Skib				if (so->so_rcv.sb_cc)
607177997Skib					so->so_oobmark = so->so_rcv.sb_cc;
608177997Skib				else
60983366Sjulian					so->so_rcv.sb_state |= SBS_RCVATMARK;
61014331Speter			}
611102814Siedowse			q = q->si_prev;
612102814Siedowse			remque(q->si_next);
613162201Snetchild			wakeup = 1;
61414331Speter			spxstat.spxs_rcvpack++;
615102814Siedowse#ifdef SF_NEWCALL
61614331Speter			if (cb->s_flags2 & SF_NEWCALL) {
61714331Speter				struct spxhdr *sp = mtod(m, struct spxhdr *);
61872543Sjlemon				u_char dt = sp->spx_dt;
619102814Siedowse				spx_newchecks[4]++;
62014331Speter				if (dt != cb->s_rhdr.spx_dt) {
62114331Speter					struct mbuf *mm =
622102814Siedowse					   m_getclr(M_DONTWAIT, MT_CONTROL);
623162201Snetchild					spx_newchecks[0]++;
624162201Snetchild					if (mm != NULL) {
625162201Snetchild						u_short *s =
626162201Snetchild							mtod(mm, u_short *);
627162201Snetchild						cb->s_rhdr.spx_dt = dt;
628102814Siedowse						mm->m_len = 5; /*XXX*/
629102814Siedowse						s[0] = 5;
63014331Speter						s[1] = 1;
63114331Speter						*(u_char *)(&s[2]) = dt;
63214331Speter						sbappend_locked(&so->so_rcv, mm);
633177997Skib					}
634177997Skib				}
635177997Skib				if (sp->spx_cc & SPX_OB) {
636177997Skib					MCHTYPE(m, MT_OOBDATA);
637177997Skib					spx_newchecks[1]++;
638177997Skib					so->so_oobmark = 0;
639177997Skib					so->so_rcv.sb_state &= ~SBS_RCVATMARK;
640177997Skib				}
641177997Skib				if (packetp == 0) {
642177997Skib					m->m_data += SPINC;
643177997Skib					m->m_len -= SPINC;
644177997Skib					m->m_pkthdr.len -= SPINC;
645177997Skib				}
646177997Skib				if ((sp->spx_cc & SPX_EM) || packetp) {
647177997Skib					sbappendrecord_locked(&so->so_rcv, m);
648177997Skib					spx_newchecks[9]++;
649177997Skib				} else
650177997Skib					sbappend_locked(&so->so_rcv, m);
651177997Skib			} else
652177997Skib#endif
653202113Smckusick			if (packetp) {
654177997Skib				sbappendrecord_locked(&so->so_rcv, m);
655177997Skib			} else {
656177997Skib				cb->s_rhdr = *mtod(m, struct spxhdr *);
657177997Skib				m->m_data += SPINC;
658177997Skib				m->m_len -= SPINC;
659177997Skib				m->m_pkthdr.len -= SPINC;
660177997Skib				sbappend_locked(&so->so_rcv, m);
661177997Skib			}
662177997Skib		  } else
663177997Skib			break;
66483366Sjulian	}
66514331Speter	if (wakeup)
666102814Siedowse		sorwakeup_locked(so);
667102814Siedowse	else
66814331Speter		SOCKBUF_UNLOCK(&so->so_rcv);
669102814Siedowse	return (0);
67014331Speter}
67114331Speter
67272543Sjlemonvoid
673102814Siedowsespx_ctlinput(cmd, arg_as_sa, dummy)
67414331Speter	int cmd;
675102814Siedowse	struct sockaddr *arg_as_sa;	/* XXX should be swapped with dummy */
676102814Siedowse	void *dummy;
677102814Siedowse{
67814331Speter	caddr_t arg = (/* XXX */ caddr_t)arg_as_sa;
67914331Speter	struct ipx_addr *na;
68014331Speter	struct sockaddr_ipx *sipx;
68183366Sjulian
68214331Speter	if (cmd < 0 || cmd >= PRC_NCMDS)
683102814Siedowse		return;
684102814Siedowse
68514331Speter	switch (cmd) {
686102814Siedowse
68714331Speter	case PRC_ROUTEDEAD:
68814331Speter		return;
68972543Sjlemon
690102814Siedowse	case PRC_IFDOWN:
69114331Speter	case PRC_HOSTDEAD:
692102814Siedowse	case PRC_HOSTUNREACH:
693102814Siedowse		sipx = (struct sockaddr_ipx *)arg;
694102814Siedowse		if (sipx->sipx_family != AF_IPX)
69514331Speter			return;
69614331Speter		na = &sipx->sipx_addr;
69714331Speter		break;
698177997Skib
699177997Skib	default:
700177997Skib		break;
701177997Skib	}
702177997Skib}
703177997Skib
704177997Skibstatic int
705177997Skibspx_output(cb, m0)
706177997Skib	register struct spxpcb *cb;
707177997Skib	struct mbuf *m0;
708177997Skib{
709177997Skib	struct socket *so = cb->s_ipxpcb->ipxp_socket;
710177997Skib	register struct mbuf *m;
711177997Skib	register struct spx *si = NULL;
712177997Skib	register struct sockbuf *sb = &so->so_snd;
713177997Skib	int len = 0, win, rcv_win;
714177997Skib	short span, off, recordp = 0;
715177997Skib	u_short alo;
716177997Skib	int error = 0, sendalot;
71783366Sjulian#ifdef notdef
71814331Speter	int idle;
719102814Siedowse#endif
720102814Siedowse	struct mbuf *mprev;
72114331Speter
722102814Siedowse	if (m0 != NULL) {
72314331Speter		int mtu = cb->s_mtu;
72414331Speter		int datalen;
72572543Sjlemon		/*
726102814Siedowse		 * Make sure that packet isn't too big.
72714331Speter		 */
728102814Siedowse		for (m = m0; m != NULL; m = m->m_next) {
729102814Siedowse			mprev = m;
730102814Siedowse			len += m->m_len;
73114331Speter			if (m->m_flags & M_EOR)
73214331Speter				recordp = 1;
73314331Speter		}
734177997Skib		datalen = (cb->s_flags & SF_HO) ?
735177997Skib				len - sizeof(struct spxhdr) : len;
736177997Skib		if (datalen > mtu) {
737177997Skib			if (cb->s_flags & SF_PI) {
738177997Skib				m_freem(m0);
739177997Skib				return (EMSGSIZE);
740177997Skib			} else {
741177997Skib				int oldEM = cb->s_cc & SPX_EM;
742177997Skib
743177997Skib				cb->s_cc &= ~SPX_EM;
744177997Skib				while (len > mtu) {
745177997Skib					/*
746177997Skib					 * Here we are only being called
747177997Skib					 * from usrreq(), so it is OK to
748177997Skib					 * block.
749177997Skib					 */
750177997Skib					m = m_copym(m0, 0, mtu, M_TRYWAIT);
751177997Skib					if (cb->s_flags & SF_NEWCALL) {
75283366Sjulian					    struct mbuf *mm = m;
75314331Speter					    spx_newchecks[7]++;
754102814Siedowse					    while (mm != NULL) {
755102814Siedowse						mm->m_flags &= ~M_EOR;
75614331Speter						mm = mm->m_next;
757102814Siedowse					    }
75814331Speter					}
75914331Speter					error = spx_output(cb, m);
76072543Sjlemon					if (error) {
761102814Siedowse						cb->s_cc |= oldEM;
76214331Speter						m_freem(m0);
763102814Siedowse						return (error);
764102814Siedowse					}
765102814Siedowse					m_adj(m0, mtu);
76614331Speter					len -= mtu;
76714331Speter				}
76814331Speter				cb->s_cc |= oldEM;
76983366Sjulian			}
77014331Speter		}
771102814Siedowse		/*
772102814Siedowse		 * Force length even, by adding a "garbage byte" if
77314331Speter		 * necessary.
774102814Siedowse		 */
775102814Siedowse		if (len & 1) {
776177997Skib			m = mprev;
777102814Siedowse			if (M_TRAILINGSPACE(m) >= 1)
778102814Siedowse				m->m_len++;
779102814Siedowse			else {
780102814Siedowse				struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
78114331Speter
78214331Speter				if (m1 == NULL) {
78372543Sjlemon					m_freem(m0);
784102814Siedowse					return (ENOBUFS);
78514331Speter				}
786102814Siedowse				m1->m_len = 1;
787102814Siedowse				*(mtod(m1, u_char *)) = 0;
788102814Siedowse				m->m_next = m1;
789102814Siedowse			}
79014331Speter		}
79114331Speter		m = m_gethdr(M_DONTWAIT, MT_HEADER);
79214331Speter		if (m == NULL) {
793177997Skib			m_freem(m0);
794177997Skib			return (ENOBUFS);
795177997Skib		}
796177997Skib		/*
797177997Skib		 * Fill in mbuf with extended SP header
798177997Skib		 * and addresses and length put into network format.
799177997Skib		 */
800177997Skib		MH_ALIGN(m, sizeof(struct spx));
801177997Skib		m->m_len = sizeof(struct spx);
802177997Skib		m->m_next = m0;
803177997Skib		si = mtod(m, struct spx *);
804177997Skib		si->si_i = *cb->s_ipx;
805177997Skib		si->si_s = cb->s_shdr;
806177997Skib		if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) {
807177997Skib			register struct spxhdr *sh;
808177997Skib			if (m0->m_len < sizeof(*sh)) {
809177997Skib				if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) {
810177997Skib					m_free(m);
811177997Skib					m_freem(m0);
812177997Skib					return (EINVAL);
813177997Skib				}
814177997Skib				m->m_next = m0;
815177997Skib			}
816177997Skib			sh = mtod(m0, struct spxhdr *);
817177997Skib			si->si_dt = sh->spx_dt;
818177997Skib			si->si_cc |= sh->spx_cc & SPX_EM;
81983366Sjulian			m0->m_len -= sizeof(*sh);
82014331Speter			m0->m_data += sizeof(*sh);
821102814Siedowse			len -= sizeof(*sh);
822102814Siedowse		}
82314331Speter		len += sizeof(*si);
824102814Siedowse		if ((cb->s_flags2 & SF_NEWCALL) && recordp) {
825102814Siedowse			si->si_cc |= SPX_EM;
826177997Skib			spx_newchecks[8]++;
827102814Siedowse		}
828102814Siedowse		if (cb->s_oobflags & SF_SOOB) {
829102814Siedowse			/*
830102814Siedowse			 * Per jqj@cornell:
83114331Speter			 * make sure OB packets convey exactly 1 byte.
83214331Speter			 * If the packet is 1 byte or larger, we
83372543Sjlemon			 * have already guaranted there to be at least
834102814Siedowse			 * one garbage byte for the checksum, and
83514331Speter			 * extra bytes shouldn't hurt!
836102814Siedowse			 */
837102814Siedowse			if (len > sizeof(*si)) {
838102814Siedowse				si->si_cc |= SPX_OB;
839102814Siedowse				len = (1 + sizeof(*si));
84014331Speter			}
84114331Speter		}
84214331Speter		si->si_len = htons((u_short)len);
843177997Skib		m->m_pkthdr.len = ((len - 1) | 1) + 1;
844177997Skib		/*
845177997Skib		 * queue stuff up for output
846177997Skib		 */
847177997Skib		sbappendrecord(sb, m);
848177997Skib		cb->s_seq++;
849177997Skib	}
850177997Skib#ifdef notdef
851177997Skib	idle = (cb->s_smax == (cb->s_rack - 1));
852177997Skib#endif
853177997Skibagain:
854177997Skib	sendalot = 0;
855177997Skib	off = cb->s_snxt - cb->s_rack;
856177997Skib	win = min(cb->s_swnd, (cb->s_cwnd / CUNIT));
857177997Skib
858177997Skib	/*
859177997Skib	 * If in persist timeout with window of 0, send a probe.
860177997Skib	 * Otherwise, if window is small but nonzero
861177997Skib	 * and timer expired, send what we can and go into
862177997Skib	 * transmit state.
863177997Skib	 */
864177997Skib	if (cb->s_force == 1 + SPXT_PERSIST) {
865177997Skib		if (win != 0) {
866177997Skib			cb->s_timer[SPXT_PERSIST] = 0;
867177997Skib			cb->s_rxtshift = 0;
868177997Skib		}
86983366Sjulian	}
87014331Speter	span = cb->s_seq - cb->s_rack;
871102814Siedowse	len = min(span, win) - off;
872102814Siedowse
87314331Speter	if (len < 0) {
874102814Siedowse		/*
87514331Speter		 * Window shrank after we went into it.
87614331Speter		 * If window shrank to 0, cancel pending
87772543Sjlemon		 * restransmission and pull s_snxt back
878102814Siedowse		 * to (closed) window.  We will enter persist
879102814Siedowse		 * state below.  If the widndow didn't close completely,
88014331Speter		 * just wait for an ACK.
881102814Siedowse		 */
882102814Siedowse		len = 0;
883102814Siedowse		if (win == 0) {
884102814Siedowse			cb->s_timer[SPXT_REXMT] = 0;
88514331Speter			cb->s_snxt = cb->s_rack;
88614331Speter		}
88714331Speter	}
888177997Skib	if (len > 1)
889177997Skib		sendalot = 1;
890177997Skib	rcv_win = sbspace(&so->so_rcv);
891177997Skib
892177997Skib	/*
893177997Skib	 * Send if we owe peer an ACK.
894177997Skib	 */
895177997Skib	if (cb->s_oobflags & SF_SOOB) {
896177997Skib		/*
897177997Skib		 * must transmit this out of band packet
898177997Skib		 */
899177997Skib		cb->s_oobflags &= ~ SF_SOOB;
900177997Skib		sendalot = 1;
901177997Skib		spxstat.spxs_sndurg++;
902177997Skib		goto found;
903177997Skib	}
904177997Skib	if (cb->s_flags & SF_ACKNOW)
905177997Skib		goto send;
906177997Skib	if (cb->s_state < TCPS_ESTABLISHED)
907178439Srdivacky		goto send;
908177997Skib	/*
90983366Sjulian	 * Silly window can't happen in spx.
91014331Speter	 * Code from tcp deleted.
911102814Siedowse	 */
912102814Siedowse	if (len)
91314331Speter		goto send;
914102814Siedowse	/*
91514331Speter	 * Compare available window to amount of window
91614331Speter	 * known to peer (as advertised window less
91772543Sjlemon	 * next expected input.)  If the difference is at least two
918102814Siedowse	 * packets or at least 35% of the mximum possible window,
91914331Speter	 * then want to send a window update to peer.
92014331Speter	 */
921102814Siedowse	if (rcv_win > 0) {
922102814Siedowse		u_short delta =  1 + cb->s_alo - cb->s_ack;
923102814Siedowse		int adv = rcv_win - (delta * cb->s_mtu);
92414331Speter
92514331Speter		if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) ||
92649662Smarcel		    (100 * adv / so->so_rcv.sb_hiwat >= 35)) {
927178439Srdivacky			spxstat.spxs_sndwinup++;
928178439Srdivacky			cb->s_flags |= SF_ACKNOW;
929178439Srdivacky			goto send;
930178439Srdivacky		}
931178439Srdivacky
932178439Srdivacky	}
933178439Srdivacky	/*
934178439Srdivacky	 * Many comments from tcp_output.c are appropriate here
935178439Srdivacky	 * including . . .
936178439Srdivacky	 * If send window is too small, there is data to transmit, and no
937178439Srdivacky	 * retransmit or persist is pending, then go to persist state.
938178439Srdivacky	 * If nothing happens soon, send when timer expires:
939178439Srdivacky	 * if window is nonzero, transmit what we can,
940178439Srdivacky	 * otherwise send a probe.
941178439Srdivacky	 */
942178439Srdivacky	if (so->so_snd.sb_cc && cb->s_timer[SPXT_REXMT] == 0 &&
943178439Srdivacky		cb->s_timer[SPXT_PERSIST] == 0) {
944156842Snetchild			cb->s_rxtshift = 0;
945156842Snetchild			spx_setpersist(cb);
946156842Snetchild	}
947156842Snetchild	/*
948156842Snetchild	 * No reason to send a packet, just return.
949156842Snetchild	 */
950156842Snetchild	cb->s_outx = 1;
951156842Snetchild	return (0);
952156842Snetchild
953156842Snetchildsend:
954225617Skmacy	/*
955156842Snetchild	 * Find requested packet.
956156842Snetchild	 */
957156842Snetchild	si = 0;
95883366Sjulian	if (len > 0) {
95949662Smarcel		cb->s_want = cb->s_snxt;
960102814Siedowse		for (m = sb->sb_mb; m != NULL; m = m->m_act) {
961102814Siedowse			si = mtod(m, struct spx *);
96249662Smarcel			if (SSEQ_LEQ(cb->s_snxt, si->si_seq))
963102814Siedowse				break;
964102814Siedowse		}
965177997Skib	found:
966102814Siedowse		if (si != NULL) {
967102814Siedowse			if (si->si_seq == cb->s_snxt)
968102814Siedowse					cb->s_snxt++;
969102814Siedowse				else
97049662Smarcel					spxstat.spxs_sndvoid++, si = 0;
97149662Smarcel		}
97272543Sjlemon	}
973102814Siedowse	/*
97449662Smarcel	 * update window
975102814Siedowse	 */
976102814Siedowse	if (rcv_win < 0)
977102814Siedowse		rcv_win = 0;
978102814Siedowse	alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu));
97949662Smarcel	if (SSEQ_LT(alo, cb->s_alo))
98049788Smarcel		alo = cb->s_alo;
98153713Smarcel
982177997Skib	if (si != NULL) {
983177997Skib		/*
984177997Skib		 * must make a copy of this packet for
985227693Sed		 * ipx_output to monkey with
986177997Skib		 */
987227693Sed		m = m_copy(dtom(si), 0, (int)M_COPYALL);
988177997Skib		if (m == NULL) {
989177997Skib			return (ENOBUFS);
990177997Skib		}
991177997Skib		si = mtod(m, struct spx *);
992177997Skib		if (SSEQ_LT(si->si_seq, cb->s_smax))
993177997Skib			spxstat.spxs_sndrexmitpack++;
994177997Skib		else
995177997Skib			spxstat.spxs_sndpack++;
996177997Skib	} else if (cb->s_force || cb->s_flags & SF_ACKNOW) {
997177997Skib		/*
998177997Skib		 * Must send an acknowledgement or a probe
999177997Skib		 */
1000177997Skib		if (cb->s_force)
1001177997Skib			spxstat.spxs_sndprobe++;
1002177997Skib		if (cb->s_flags & SF_ACKNOW)
1003227693Sed			spxstat.spxs_sndacks++;
1004177997Skib		m = m_gethdr(M_DONTWAIT, MT_HEADER);
1005177997Skib		if (m == NULL)
1006227693Sed			return (ENOBUFS);
1007227693Sed		/*
1008227693Sed		 * Fill in mbuf with extended SP header
1009177997Skib		 * and addresses and length put into network format.
1010177997Skib		 */
1011177997Skib		MH_ALIGN(m, sizeof(struct spx));
1012177997Skib		m->m_len = sizeof(*si);
1013177997Skib		m->m_pkthdr.len = sizeof(*si);
1014177997Skib		si = mtod(m, struct spx *);
101583366Sjulian		si->si_i = *cb->s_ipx;
101683366Sjulian		si->si_s = cb->s_shdr;
101753713Smarcel		si->si_seq = cb->s_smax + 1;
101853713Smarcel		si->si_len = htons(sizeof(*si));
101953713Smarcel		si->si_cc |= SPX_SP;
102053713Smarcel	} else {
102153713Smarcel		cb->s_outx = 3;
1022225617Skmacy		if (so->so_options & SO_DEBUG || traceallspxs)
102353713Smarcel			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
102463285Smarcel		return (0);
102563285Smarcel	}
102683366Sjulian	/*
102783366Sjulian	 * Stuff checksum and output datagram.
102863285Smarcel	 */
102963285Smarcel	if ((si->si_cc & SPX_SP) == 0) {
103063285Smarcel		if (cb->s_force != (1 + SPXT_PERSIST) ||
1031255219Spjd		    cb->s_timer[SPXT_PERSIST] == 0) {
1032162585Snetchild			/*
1033162585Snetchild			 * If this is a new packet and we are not currently
103463285Smarcel			 * timing anything, time this one.
103563285Smarcel			 */
103663285Smarcel			if (SSEQ_LT(cb->s_smax, si->si_seq)) {
103763285Smarcel				cb->s_smax = si->si_seq;
103863285Smarcel				if (cb->s_rtt == 0) {
1039162585Snetchild					spxstat.spxs_segstimed++;
1040225617Skmacy					cb->s_rtseq = si->si_seq;
1041162585Snetchild					cb->s_rtt = 1;
1042162585Snetchild				}
1043247602Spjd			}
1044255219Spjd			/*
1045255219Spjd			 * Set rexmt timer if not currently set,
1046255219Spjd			 * Initial value for retransmit timer is smoothed
1047247602Spjd			 * round-trip time + 2 * round-trip time variance.
1048162585Snetchild			 * Initialize shift counter which is used for backoff
1049247602Spjd			 * of retransmit time.
1050162585Snetchild			 */
1051162585Snetchild			if (cb->s_timer[SPXT_REXMT] == 0 &&
1052162585Snetchild			    cb->s_snxt != cb->s_rack) {
1053162585Snetchild				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1054162585Snetchild				if (cb->s_timer[SPXT_PERSIST]) {
1055162585Snetchild					cb->s_timer[SPXT_PERSIST] = 0;
105663285Smarcel					cb->s_rxtshift = 0;
105763285Smarcel				}
105863285Smarcel			}
105983366Sjulian		} else if (SSEQ_LT(cb->s_smax, si->si_seq)) {
106083366Sjulian			cb->s_smax = si->si_seq;
106163285Smarcel		}
106263285Smarcel	} else if (cb->s_state < TCPS_ESTABLISHED) {
106363285Smarcel		if (cb->s_rtt == 0)
106463285Smarcel			cb->s_rtt = 1; /* Time initial handshake */
106563285Smarcel		if (cb->s_timer[SPXT_REXMT] == 0)
106663285Smarcel			cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
106763285Smarcel	}
106863285Smarcel	{
1069225617Skmacy		/*
107063285Smarcel		 * Do not request acks when we ack their data packets or
107172538Sjlemon		 * when we do a gratuitous window update.
107272538Sjlemon		 */
107383366Sjulian		if (((si->si_cc & SPX_SP) == 0) || cb->s_force)
107472538Sjlemon				si->si_cc |= SPX_SA;
107572538Sjlemon		si->si_seq = htons(si->si_seq);
1076111798Sdes		si->si_alo = htons(alo);
1077111798Sdes		si->si_ack = htons(cb->s_ack);
107873286Sadrian
107973286Sadrian		if (ipxcksum) {
108073286Sadrian			si->si_sum = ipx_cksum(m, ntohs(si->si_len));
108172538Sjlemon		} else
1082111798Sdes			si->si_sum = 0xffff;
108373286Sadrian
108472538Sjlemon		cb->s_outx = 4;
1085111798Sdes		if (so->so_options & SO_DEBUG || traceallspxs)
1086127057Stjr			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
108772538Sjlemon
1088111798Sdes		if (so->so_options & SO_DONTROUTE)
1089127057Stjr			error = ipx_outputfl(m, NULL, IPX_ROUTETOIF);
109072538Sjlemon		else
1091111798Sdes			error = ipx_outputfl(m, &cb->s_ipxpcb->ipxp_route, 0);
109272538Sjlemon	}
109372538Sjlemon	if (error) {
109472538Sjlemon		return (error);
109572538Sjlemon	}
109672538Sjlemon	spxstat.spxs_sndtotal++;
109772538Sjlemon	/*
109872538Sjlemon	 * Data sent (as far as we can tell).
109972538Sjlemon	 * If this advertises a larger window than any other segment,
1100127059Stjr	 * then remember the size of the advertized window.
110173286Sadrian	 * Any pending ACK has now been sent.
110272538Sjlemon	 */
110372538Sjlemon	cb->s_force = 0;
110472538Sjlemon	cb->s_flags &= ~(SF_ACKNOW|SF_DELACK);
110572538Sjlemon	if (SSEQ_GT(alo, cb->s_alo))
110672538Sjlemon		cb->s_alo = alo;
110772538Sjlemon	if (sendalot)
1108127059Stjr		goto again;
110973286Sadrian	cb->s_outx = 5;
1110190445Sambrisko	return (0);
1111190445Sambrisko}
1112190445Sambrisko
111372538Sjlemonstatic int spx_do_persist_panics = 0;
111472538Sjlemon
111572538Sjlemonstatic void
111672538Sjlemonspx_setpersist(cb)
111773286Sadrian	register struct spxpcb *cb;
111872538Sjlemon{
111972538Sjlemon	register int t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
112072538Sjlemon
112172538Sjlemon	if (cb->s_timer[SPXT_REXMT] && spx_do_persist_panics)
112272538Sjlemon		panic("spx_output REXMT");
112372538Sjlemon	/*
112472538Sjlemon	 * Start/restart persistance timer.
1125111798Sdes	 */
112672538Sjlemon	SPXT_RANGESET(cb->s_timer[SPXT_PERSIST],
1127111798Sdes	    t*spx_backoff[cb->s_rxtshift],
112872538Sjlemon	    SPXTV_PERSMIN, SPXTV_PERSMAX);
1129111798Sdes	if (cb->s_rxtshift < SPX_MAXRXTSHIFT)
113072538Sjlemon		cb->s_rxtshift++;
1131111798Sdes}
113272538Sjlemon
113372538Sjlemonint
1134127059Stjrspx_ctloutput(so, sopt)
1135132708Sphk	struct socket *so;
1136132708Sphk	struct sockopt *sopt;
1137132708Sphk{
1138132708Sphk	struct ipxpcb *ipxp = sotoipxpcb(so);
1139190445Sambrisko	register struct spxpcb *cb;
1140190445Sambrisko	int mask, error;
1141190445Sambrisko	short soptval;
1142190445Sambrisko	u_short usoptval;
1143190445Sambrisko	int optval;
1144190445Sambrisko
1145127059Stjr	error = 0;
1146138353Sphk
1147127059Stjr	if (sopt->sopt_level != IPXPROTO_SPX) {
114872538Sjlemon		/* This will have to be changed when we do more general
114972538Sjlemon		   stacking of protocols */
115072538Sjlemon		return (ipx_ctloutput(so, sopt));
115183366Sjulian	}
115272538Sjlemon	if (ipxp == NULL)
115383221Smarcel		return (EINVAL);
115472538Sjlemon	else
115572538Sjlemon		cb = ipxtospxpcb(ipxp);
115672538Sjlemon
115783366Sjulian	switch (sopt->sopt_dir) {
115872538Sjlemon	case SOPT_GET:
115972538Sjlemon		switch (sopt->sopt_name) {
116072538Sjlemon		case SO_HEADERS_ON_INPUT:
116183366Sjulian			mask = SF_HI;
116272538Sjlemon			goto get_flags;
116372538Sjlemon
116472538Sjlemon		case SO_HEADERS_ON_OUTPUT:
116572538Sjlemon			mask = SF_HO;
116672538Sjlemon		get_flags:
1167225617Skmacy			soptval = cb->s_flags & mask;
116872538Sjlemon			error = sooptcopyout(sopt, &soptval, sizeof soptval);
116983221Smarcel			break;
117083221Smarcel
117183221Smarcel		case SO_MTU:
117283221Smarcel			usoptval = cb->s_mtu;
117383221Smarcel			error = sooptcopyout(sopt, &usoptval, sizeof usoptval);
117483221Smarcel			break;
117583221Smarcel
117683221Smarcel		case SO_LAST_HEADER:
117783221Smarcel			error = sooptcopyout(sopt, &cb->s_rhdr,
117883221Smarcel					     sizeof cb->s_rhdr);
117983221Smarcel			break;
1180133816Stjr
1181140214Sobrien		case SO_DEFAULT_HEADERS:
1182133816Stjr			error = sooptcopyout(sopt, &cb->s_shdr,
1183133816Stjr					     sizeof cb->s_shdr);
1184133816Stjr			break;
118583221Smarcel
118683221Smarcel		default:
118783221Smarcel			error = ENOPROTOOPT;
118883221Smarcel		}
118983221Smarcel		break;
119083221Smarcel
119183221Smarcel	case SOPT_SET:
119283221Smarcel		switch (sopt->sopt_name) {
119383221Smarcel			/* XXX why are these shorts on get and ints on set?
119483221Smarcel			   that doesn't make any sense... */
119583221Smarcel		case SO_HEADERS_ON_INPUT:
119683221Smarcel			mask = SF_HI;
119783221Smarcel			goto set_head;
119883221Smarcel
119983221Smarcel		case SO_HEADERS_ON_OUTPUT:
120083221Smarcel			mask = SF_HO;
120183221Smarcel		set_head:
120283221Smarcel			error = sooptcopyin(sopt, &optval, sizeof optval,
120383221Smarcel					    sizeof optval);
120483221Smarcel			if (error)
120583221Smarcel				break;
120683221Smarcel
1207177633Sdfr			if (cb->s_flags & SF_PI) {
120883221Smarcel				if (optval)
120983221Smarcel					cb->s_flags |= mask;
121083221Smarcel				else
121183221Smarcel					cb->s_flags &= ~mask;
121283221Smarcel			} else error = EINVAL;
121383221Smarcel			break;
121483221Smarcel
121583221Smarcel		case SO_MTU:
121683221Smarcel			error = sooptcopyin(sopt, &usoptval, sizeof usoptval,
121783221Smarcel					    sizeof usoptval);
121883221Smarcel			if (error)
121983221Smarcel				break;
122083221Smarcel			cb->s_mtu = usoptval;
122183221Smarcel			break;
122283221Smarcel
122383221Smarcel#ifdef SF_NEWCALL
122483221Smarcel		case SO_NEWCALL:
122583221Smarcel			error = sooptcopyin(sopt, &optval, sizeof optval,
122683221Smarcel					    sizeof optval);
122783221Smarcel			if (error)
122883221Smarcel				break;
122983221Smarcel			if (optval) {
1230140214Sobrien				cb->s_flags2 |= SF_NEWCALL;
123183221Smarcel				spx_newchecks[5]++;
123283221Smarcel			} else {
123383221Smarcel				cb->s_flags2 &= ~SF_NEWCALL;
123483221Smarcel				spx_newchecks[6]++;
123583221Smarcel			}
123683221Smarcel			break;
1237133816Stjr#endif
1238140214Sobrien
1239133816Stjr		case SO_DEFAULT_HEADERS:
1240133816Stjr			{
1241133816Stjr				struct spxhdr sp;
124283221Smarcel
124383221Smarcel				error = sooptcopyin(sopt, &sp, sizeof sp,
124483221Smarcel						    sizeof sp);
124583221Smarcel				if (error)
124683221Smarcel					break;
124783221Smarcel				cb->s_dt = sp.spx_dt;
124883221Smarcel				cb->s_cc = sp.spx_cc & SPX_EM;
124983221Smarcel			}
125083221Smarcel			break;
125183221Smarcel
125283221Smarcel		default:
125383221Smarcel			error = ENOPROTOOPT;
125483221Smarcel		}
125583221Smarcel		break;
125683221Smarcel	}
125783221Smarcel	return (error);
125883221Smarcel}
125983221Smarcel
126083221Smarcelstatic int
126183221Smarcelspx_usr_abort(so)
126283221Smarcel	struct socket *so;
126383221Smarcel{
1264177633Sdfr	int s;
126583221Smarcel	struct ipxpcb *ipxp;
126683221Smarcel	struct spxpcb *cb;
126783221Smarcel
126883221Smarcel	ipxp = sotoipxpcb(so);
126983221Smarcel	cb = ipxtospxpcb(ipxp);
127083221Smarcel
127183221Smarcel	s = splnet();
127283221Smarcel	spx_drop(cb, ECONNABORTED);
127383221Smarcel	splx(s);
127483221Smarcel	return (0);
127583221Smarcel}
127683221Smarcel
127783221Smarcel/*
127883221Smarcel * Accept a connection.  Essentially all the work is
127983221Smarcel * done at higher levels; just return the address
128083221Smarcel * of the peer, storing through addr.
128183221Smarcel */
128283221Smarcelstatic int
128383221Smarcelspx_accept(so, nam)
128483221Smarcel	struct socket *so;
128583221Smarcel	struct sockaddr **nam;
1286133816Stjr{
128783221Smarcel	struct ipxpcb *ipxp;
128883221Smarcel	struct sockaddr_ipx *sipx, ssipx;
128983366Sjulian
129083221Smarcel	ipxp = sotoipxpcb(so);
1291107680Siedowse	sipx = &ssipx;
1292107680Siedowse	bzero(sipx, sizeof *sipx);
1293255219Spjd	sipx->sipx_len = sizeof *sipx;
129483221Smarcel	sipx->sipx_family = AF_IPX;
1295102872Siedowse	sipx->sipx_addr = ipxp->ipxp_faddr;
129683221Smarcel	*nam = sodupsockaddr((struct sockaddr *)sipx, M_NOWAIT);
129783221Smarcel	return (0);
129883221Smarcel}
129983221Smarcel
1300102872Siedowsestatic int
130183221Smarcelspx_attach(so, proto, td)
130283221Smarcel	struct socket *so;
1303102872Siedowse	int proto;
130483221Smarcel	struct thread *td;
130583221Smarcel{
1306102872Siedowse	int error;
130783221Smarcel	int s;
130883221Smarcel	struct ipxpcb *ipxp;
1309102872Siedowse	struct spxpcb *cb;
131083366Sjulian	struct mbuf *mm;
131183366Sjulian	struct sockbuf *sb;
131283221Smarcel
131383366Sjulian	ipxp = sotoipxpcb(so);
131483221Smarcel	cb = ipxtospxpcb(ipxp);
131583366Sjulian
131683221Smarcel	if (ipxp != NULL)
131783366Sjulian		return (EISCONN);
131883221Smarcel	s = splnet();
131983366Sjulian	error = ipx_pcballoc(so, &ipxpcb_list, td);
132083221Smarcel	if (error)
132183366Sjulian		goto spx_attach_end;
132283221Smarcel	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
132383366Sjulian		error = soreserve(so, (u_long) 3072, (u_long) 3072);
132483221Smarcel		if (error)
132583366Sjulian			goto spx_attach_end;
1326144987Smdodd	}
1327144987Smdodd	ipxp = sotoipxpcb(so);
1328144987Smdodd
1329144987Smdodd	MALLOC(cb, struct spxpcb *, sizeof *cb, M_PCB, M_NOWAIT | M_ZERO);
1330144987Smdodd
1331144987Smdodd	if (cb == NULL) {
1332144987Smdodd		error = ENOBUFS;
1333144987Smdodd		goto spx_attach_end;
133483221Smarcel	}
133583221Smarcel	sb = &so->so_snd;
133683221Smarcel
1337102872Siedowse	mm = m_getclr(M_DONTWAIT, MT_HEADER);
133883221Smarcel	if (mm == NULL) {
1339102872Siedowse		FREE(cb, M_PCB);
134083221Smarcel		error = ENOBUFS;
1341102872Siedowse		goto spx_attach_end;
134283221Smarcel	}
1343102872Siedowse	cb->s_ipx = mtod(mm, struct ipx *);
134483221Smarcel	cb->s_state = TCPS_LISTEN;
1345102872Siedowse	cb->s_smax = -1;
1346144987Smdodd	cb->s_swl1 = -1;
1347144987Smdodd	cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q;
1348144987Smdodd	cb->s_ipxpcb = ipxp;
1349144987Smdodd	cb->s_mtu = 576 - sizeof(struct spx);
1350144987Smdodd	cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu;
1351144987Smdodd	cb->s_ssthresh = cb->s_cwnd;
1352144987Smdodd	cb->s_cwmx = sbspace(sb) * CUNIT / (2 * sizeof(struct spx));
1353144987Smdodd	/* Above is recomputed when connecting to account
1354102872Siedowse	   for changed buffering or mtu's */
135583221Smarcel	cb->s_rtt = SPXTV_SRTTBASE;
1356107680Siedowse	cb->s_rttvar = SPXTV_SRTTDFLT << 2;
1357111797Sdes	SPXT_RANGESET(cb->s_rxtcur,
1358107680Siedowse	    ((SPXTV_SRTTBASE >> 2) + (SPXTV_SRTTDFLT << 2)) >> 1,
1359107680Siedowse	    SPXTV_MIN, SPXTV_REXMTMAX);
1360107680Siedowse	ipxp->ipxp_pcb = (caddr_t)cb;
1361107680Siedowsespx_attach_end:
1362107680Siedowse	splx(s);
1363107680Siedowse	return (error);
1364107680Siedowse}
1365107680Siedowse
1366111797Sdesstatic int
1367107680Siedowsespx_bind(so, nam, td)
1368107680Siedowse	struct socket *so;
1369107680Siedowse	struct sockaddr *nam;
1370111797Sdes	struct thread *td;
1371107680Siedowse{
1372107680Siedowse	struct ipxpcb *ipxp;
1373107680Siedowse
1374107680Siedowse	ipxp = sotoipxpcb(so);
1375107680Siedowse
1376107680Siedowse	return (ipx_pcbbind(ipxp, nam, td));
1377107680Siedowse}
1378107680Siedowse
1379111797Sdes/*
1380107680Siedowse * Initiate connection to peer.
1381107680Siedowse * Enter SYN_SENT state, and mark socket as connecting.
1382107680Siedowse * Start keep-alive timer, setup prototype header,
1383107680Siedowse * Send initial system packet requesting connection.
1384107680Siedowse */
1385107680Siedowsestatic int
1386107680Siedowsespx_connect(so, nam, td)
138783221Smarcel	struct socket *so;
1388102872Siedowse	struct sockaddr *nam;
138983221Smarcel	struct thread *td;
139083221Smarcel{
139183221Smarcel	int error;
139283221Smarcel	int s;
139383221Smarcel	struct ipxpcb *ipxp;
139483221Smarcel	struct spxpcb *cb;
139583221Smarcel
1396255219Spjd	ipxp = sotoipxpcb(so);
1397255219Spjd	cb = ipxtospxpcb(ipxp);
139889319Salfred
139989319Salfred	s = splnet();
140089306Salfred	if (ipxp->ipxp_lport == 0) {
140189306Salfred		error = ipx_pcbbind(ipxp, NULL, td);
140283221Smarcel		if (error)
140389306Salfred			goto spx_connect_end;
140489306Salfred	}
140583221Smarcel	error = ipx_pcbconnect(ipxp, nam, td);
1406102872Siedowse	if (error)
140783221Smarcel		goto spx_connect_end;
140883221Smarcel	soisconnecting(so);
140983221Smarcel	spxstat.spxs_connattempt++;
141083221Smarcel	cb->s_state = TCPS_SYN_SENT;
141183221Smarcel	cb->s_did = 0;
141283221Smarcel	spx_template(cb);
141383366Sjulian	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
141483221Smarcel	cb->s_force = 1 + SPXTV_KEEP;
141583221Smarcel	/*
141683221Smarcel	 * Other party is required to respond to
141783221Smarcel	 * the port I send from, but he is not
141883221Smarcel	 * required to answer from where I am sending to,
141983221Smarcel	 * so allow wildcarding.
142083221Smarcel	 * original port I am sending to is still saved in
142183221Smarcel	 * cb->s_dport.
142283221Smarcel	 */
142383221Smarcel	ipxp->ipxp_fport = 0;
142483221Smarcel	error = spx_output(cb, NULL);
142583366Sjulianspx_connect_end:
142683221Smarcel	splx(s);
142783221Smarcel	return (error);
1428140214Sobrien}
142983221Smarcel
143083366Sjulianstatic int
143183221Smarcelspx_detach(so)
143283221Smarcel	struct socket *so;
1433102872Siedowse{
143483221Smarcel	int s;
143583221Smarcel	struct ipxpcb *ipxp;
143683221Smarcel	struct spxpcb *cb;
143783221Smarcel
143883221Smarcel	ipxp = sotoipxpcb(so);
143983221Smarcel	cb = ipxtospxpcb(ipxp);
144083221Smarcel
144183221Smarcel	if (ipxp == NULL)
144299687Srobert		return (ENOTCONN);
1443111797Sdes	s = splnet();
144483221Smarcel	if (cb->s_state > TCPS_LISTEN)
144583221Smarcel		spx_disconnect(cb);
144683221Smarcel	else
1447102872Siedowse		spx_close(cb);
1448102872Siedowse	splx(s);
144983221Smarcel	return (0);
145083221Smarcel}
1451102872Siedowse
1452111797Sdes/*
1453111797Sdes * We may decide later to implement connection closing
145483221Smarcel * handshaking at the spx level optionally.
145599687Srobert * here is the hook to do it:
1456111797Sdes */
145783221Smarcelstatic int
145883221Smarcelspx_usr_disconnect(so)
145983221Smarcel	struct socket *so;
1460102872Siedowse{
1461102872Siedowse	int s;
1462102872Siedowse	struct ipxpcb *ipxp;
146383221Smarcel	struct spxpcb *cb;
146499687Srobert
1465111797Sdes	ipxp = sotoipxpcb(so);
146683221Smarcel	cb = ipxtospxpcb(ipxp);
146783221Smarcel
146883221Smarcel	s = splnet();
1469102872Siedowse	spx_disconnect(cb);
1470102872Siedowse	splx(s);
1471102872Siedowse	return (0);
147283221Smarcel}
147383221Smarcel
147483366Sjulianstatic int
147583221Smarcelspx_listen(so, td)
1476133816Stjr	struct socket *so;
147785022Smarcel	struct thread *td;
147885022Smarcel{
147985022Smarcel	int error;
148085022Smarcel	struct ipxpcb *ipxp;
1481102814Siedowse	struct spxpcb *cb;
1482102814Siedowse
148385022Smarcel	error = 0;
1484102814Siedowse	ipxp = sotoipxpcb(so);
148585022Smarcel	cb = ipxtospxpcb(ipxp);
148685022Smarcel
148785022Smarcel	if (ipxp->ipxp_lport == 0)
1488102814Siedowse		error = ipx_pcbbind(ipxp, NULL, td);
148985022Smarcel	if (error == 0)
1490102814Siedowse		cb->s_state = TCPS_LISTEN;
1491102814Siedowse	return (error);
1492102814Siedowse}
149385022Smarcel
149485022Smarcel/*
149585022Smarcel * After a receive, possibly send acknowledgment
1496177997Skib * updating allocation.
1497177997Skib */
1498177997Skibstatic int
1499227693Sedspx_rcvd(so, flags)
1500177997Skib	struct socket *so;
1501177997Skib	int flags;
1502177997Skib{
1503177997Skib	int s;
1504177997Skib	struct ipxpcb *ipxp;
1505177997Skib	struct spxpcb *cb;
1506177997Skib
1507177997Skib	ipxp = sotoipxpcb(so);
1508177997Skib	cb = ipxtospxpcb(ipxp);
1509177997Skib
1510177997Skib	s = splnet();
1511177997Skib	cb->s_flags |= SF_RVD;
1512227693Sed	spx_output(cb, NULL);
1513177997Skib	cb->s_flags &= ~SF_RVD;
1514177997Skib	splx(s);
1515227693Sed	return (0);
1516177997Skib}
1517177997Skib
1518177997Skibstatic int
1519177997Skibspx_rcvoob(so, m, flags)
1520177997Skib	struct socket *so;
152185022Smarcel	struct mbuf *m;
152285022Smarcel	int flags;
1523102814Siedowse{
1524102814Siedowse	struct ipxpcb *ipxp;
152585022Smarcel	struct spxpcb *cb;
1526102814Siedowse
152785022Smarcel	ipxp = sotoipxpcb(so);
152885022Smarcel	cb = ipxtospxpcb(ipxp);
152985022Smarcel
1530102814Siedowse	SOCKBUF_LOCK(&so->so_rcv);
153185022Smarcel	if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark ||
1532102814Siedowse	    (so->so_rcv.sb_state & SBS_RCVATMARK)) {
1533102814Siedowse		SOCKBUF_UNLOCK(&so->so_rcv);
1534102814Siedowse		m->m_len = 1;
153585022Smarcel		*mtod(m, caddr_t) = cb->s_iobc;
1536228957Sjhb		return (0);
1537228957Sjhb	}
1538228957Sjhb	SOCKBUF_UNLOCK(&so->so_rcv);
1539228957Sjhb	return (EINVAL);
1540228957Sjhb}
1541228957Sjhb
1542228957Sjhbstatic int
1543228957Sjhbspx_send(so, flags, m, addr, controlp, td)
1544228957Sjhb	struct socket *so;
1545228957Sjhb	int flags;
1546228957Sjhb	struct mbuf *m;
1547228957Sjhb	struct sockaddr *addr;
1548228957Sjhb	struct mbuf *controlp;
1549228957Sjhb	struct thread *td;
1550228957Sjhb{
1551228957Sjhb	int error;
1552228957Sjhb	int s;
1553228957Sjhb	struct ipxpcb *ipxp;
1554228957Sjhb	struct spxpcb *cb;
1555228957Sjhb
1556228957Sjhb	error = 0;
1557228957Sjhb	ipxp = sotoipxpcb(so);
1558228957Sjhb	cb = ipxtospxpcb(ipxp);
1559228957Sjhb
1560228957Sjhb	s = splnet();
1561228957Sjhb	if (flags & PRUS_OOB) {
1562228957Sjhb		if (sbspace(&so->so_snd) < -512) {
1563228957Sjhb			error = ENOBUFS;
1564228957Sjhb			goto spx_send_end;
1565228957Sjhb		}
1566228957Sjhb		cb->s_oobflags |= SF_SOOB;
1567228957Sjhb	}
1568228957Sjhb	if (controlp != NULL) {
1569228957Sjhb		u_short *p = mtod(controlp, u_short *);
1570228957Sjhb		spx_newchecks[2]++;
1571228957Sjhb		if ((p[0] == 5) && (p[1] == 1)) { /* XXXX, for testing */
1572228957Sjhb			cb->s_shdr.spx_dt = *(u_char *)(&p[2]);
1573228957Sjhb			spx_newchecks[3]++;
1574228957Sjhb		}
1575228957Sjhb		m_freem(controlp);
1576228957Sjhb	}
1577228957Sjhb	controlp = NULL;
1578228957Sjhb	error = spx_output(cb, m);
1579228957Sjhb	m = NULL;
1580228957Sjhbspx_send_end:
1581234352Sjkim	if (controlp != NULL)
1582234352Sjkim		m_freem(controlp);
1583234352Sjkim	if (m != NULL)
1584234352Sjkim		m_freem(m);
1585234352Sjkim	splx(s);
1586234352Sjkim	return (error);
1587234352Sjkim}
1588234352Sjkim
1589234352Sjkimstatic int
1590234352Sjkimspx_shutdown(so)
1591234352Sjkim	struct socket *so;
1592234352Sjkim{
1593248951Sjilles	int error;
1594234352Sjkim	int s;
1595234352Sjkim	struct ipxpcb *ipxp;
1596234352Sjkim	struct spxpcb *cb;
1597234352Sjkim
1598234352Sjkim	error = 0;
1599234352Sjkim	ipxp = sotoipxpcb(so);
1600234352Sjkim	cb = ipxtospxpcb(ipxp);
1601234352Sjkim
1602234352Sjkim	s = splnet();
1603234352Sjkim	socantsendmore(so);
1604234352Sjkim	cb = spx_usrclosed(cb);
1605234352Sjkim	if (cb != NULL)
1606234352Sjkim		error = spx_output(cb, NULL);
1607234352Sjkim	splx(s);
1608234352Sjkim	return (error);
1609234352Sjkim}
1610234352Sjkim
1611234352Sjkimstatic int
1612234352Sjkimspx_sp_attach(so, proto, td)
1613234352Sjkim	struct socket *so;
1614234352Sjkim	int proto;
1615234352Sjkim	struct thread *td;
1616234352Sjkim{
1617234352Sjkim	int error;
1618234352Sjkim	struct ipxpcb *ipxp;
1619234352Sjkim
1620248951Sjilles	error = spx_attach(so, proto, td);
1621234352Sjkim	if (error == 0) {
1622234352Sjkim		ipxp = sotoipxpcb(so);
1623234352Sjkim		((struct spxpcb *)ipxp->ipxp_pcb)->s_flags |=
1624234352Sjkim					(SF_HI | SF_HO | SF_PI);
1625234352Sjkim	}
1626234352Sjkim	return (error);
1627}
1628
1629/*
1630 * Create template to be used to send spx packets on a connection.
1631 * Called after host entry created, fills
1632 * in a skeletal spx header (choosing connection id),
1633 * minimizing the amount of work necessary when the connection is used.
1634 */
1635static void
1636spx_template(cb)
1637	register struct spxpcb *cb;
1638{
1639	register struct ipxpcb *ipxp = cb->s_ipxpcb;
1640	register struct ipx *ipx = cb->s_ipx;
1641	register struct sockbuf *sb = &(ipxp->ipxp_socket->so_snd);
1642
1643	ipx->ipx_pt = IPXPROTO_SPX;
1644	ipx->ipx_sna = ipxp->ipxp_laddr;
1645	ipx->ipx_dna = ipxp->ipxp_faddr;
1646	cb->s_sid = htons(spx_iss);
1647	spx_iss += SPX_ISSINCR/2;
1648	cb->s_alo = 1;
1649	cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu;
1650	cb->s_ssthresh = cb->s_cwnd; /* Try to expand fast to full complement
1651					of large packets */
1652	cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spx));
1653	cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd);
1654		/* But allow for lots of little packets as well */
1655}
1656
1657/*
1658 * Close a SPIP control block:
1659 *	discard spx control block itself
1660 *	discard ipx protocol control block
1661 *	wake up any sleepers
1662 */
1663static struct spxpcb *
1664spx_close(cb)
1665	register struct spxpcb *cb;
1666{
1667	register struct spx_q *s;
1668	struct ipxpcb *ipxp = cb->s_ipxpcb;
1669	struct socket *so = ipxp->ipxp_socket;
1670	register struct mbuf *m;
1671
1672	s = cb->s_q.si_next;
1673	while (s != &(cb->s_q)) {
1674		s = s->si_next;
1675		m = dtom(s->si_prev);
1676		remque(s->si_prev);
1677		m_freem(m);
1678	}
1679	m_free(dtom(cb->s_ipx));
1680	FREE(cb, M_PCB);
1681	ipxp->ipxp_pcb = NULL;
1682	soisdisconnected(so);
1683	ipx_pcbdetach(ipxp);
1684	spxstat.spxs_closed++;
1685	return (NULL);
1686}
1687
1688/*
1689 *	Someday we may do level 3 handshaking
1690 *	to close a connection or send a xerox style error.
1691 *	For now, just close.
1692 */
1693static struct spxpcb *
1694spx_usrclosed(cb)
1695	register struct spxpcb *cb;
1696{
1697	return (spx_close(cb));
1698}
1699
1700static struct spxpcb *
1701spx_disconnect(cb)
1702	register struct spxpcb *cb;
1703{
1704	return (spx_close(cb));
1705}
1706
1707/*
1708 * Drop connection, reporting
1709 * the specified error.
1710 */
1711static struct spxpcb *
1712spx_drop(cb, errno)
1713	register struct spxpcb *cb;
1714	int errno;
1715{
1716	struct socket *so = cb->s_ipxpcb->ipxp_socket;
1717
1718	/*
1719	 * someday, in the xerox world
1720	 * we will generate error protocol packets
1721	 * announcing that the socket has gone away.
1722	 */
1723	if (TCPS_HAVERCVDSYN(cb->s_state)) {
1724		spxstat.spxs_drops++;
1725		cb->s_state = TCPS_CLOSED;
1726		/*tcp_output(cb);*/
1727	} else
1728		spxstat.spxs_conndrops++;
1729	so->so_error = errno;
1730	return (spx_close(cb));
1731}
1732
1733/*
1734 * Fast timeout routine for processing delayed acks
1735 */
1736void
1737spx_fasttimo()
1738{
1739	register struct ipxpcb *ipxp;
1740	register struct spxpcb *cb;
1741	int s = splnet();
1742
1743	LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) {
1744		if ((cb = (struct spxpcb *)ipxp->ipxp_pcb) != NULL &&
1745		    (cb->s_flags & SF_DELACK)) {
1746			cb->s_flags &= ~SF_DELACK;
1747			cb->s_flags |= SF_ACKNOW;
1748			spxstat.spxs_delack++;
1749			spx_output(cb, NULL);
1750		}
1751	}
1752
1753	splx(s);
1754}
1755
1756/*
1757 * spx protocol timeout routine called every 500 ms.
1758 * Updates the timers in all active pcb's and
1759 * causes finite state machine actions if timers expire.
1760 */
1761void
1762spx_slowtimo()
1763{
1764	register struct ipxpcb *ip, *ip_temp;
1765	register struct spxpcb *cb;
1766	int s = splnet();
1767	register int i;
1768
1769	/*
1770	 * Search through tcb's and update active timers.  Note that timers
1771	 * may free the ipxpcb, so be sure to handle that case.
1772	 */
1773	LIST_FOREACH_SAFE(ip, &ipxpcb_list, ipxp_list, ip_temp) {
1774		cb = ipxtospxpcb(ip);
1775		if (cb == NULL)
1776			continue;
1777		for (i = 0; i < SPXT_NTIMERS; i++) {
1778			if (cb->s_timer[i] && --cb->s_timer[i] == 0) {
1779				/*
1780				 * spx_timers() returns (NULL) if it free'd
1781				 * the pcb.
1782				 */
1783				cb = spx_timers(cb, i);
1784				if (cb == NULL)
1785					break;
1786			}
1787		}
1788		if (cb != NULL) {
1789			cb->s_idle++;
1790			if (cb->s_rtt)
1791				cb->s_rtt++;
1792		}
1793	}
1794	spx_iss += SPX_ISSINCR/PR_SLOWHZ;		/* increment iss */
1795	splx(s);
1796}
1797
1798/*
1799 * SPX timer processing.
1800 */
1801static struct spxpcb *
1802spx_timers(cb, timer)
1803	register struct spxpcb *cb;
1804	int timer;
1805{
1806	long rexmt;
1807	int win;
1808
1809	cb->s_force = 1 + timer;
1810	switch (timer) {
1811
1812	/*
1813	 * 2 MSL timeout in shutdown went off.  TCP deletes connection
1814	 * control block.
1815	 */
1816	case SPXT_2MSL:
1817		printf("spx: SPXT_2MSL went off for no reason\n");
1818		cb->s_timer[timer] = 0;
1819		break;
1820
1821	/*
1822	 * Retransmission timer went off.  Message has not
1823	 * been acked within retransmit interval.  Back off
1824	 * to a longer retransmit interval and retransmit one packet.
1825	 */
1826	case SPXT_REXMT:
1827		if (++cb->s_rxtshift > SPX_MAXRXTSHIFT) {
1828			cb->s_rxtshift = SPX_MAXRXTSHIFT;
1829			spxstat.spxs_timeoutdrop++;
1830			cb = spx_drop(cb, ETIMEDOUT);
1831			break;
1832		}
1833		spxstat.spxs_rexmttimeo++;
1834		rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
1835		rexmt *= spx_backoff[cb->s_rxtshift];
1836		SPXT_RANGESET(cb->s_rxtcur, rexmt, SPXTV_MIN, SPXTV_REXMTMAX);
1837		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1838		/*
1839		 * If we have backed off fairly far, our srtt
1840		 * estimate is probably bogus.  Clobber it
1841		 * so we'll take the next rtt measurement as our srtt;
1842		 * move the current srtt into rttvar to keep the current
1843		 * retransmit times until then.
1844		 */
1845		if (cb->s_rxtshift > SPX_MAXRXTSHIFT / 4 ) {
1846			cb->s_rttvar += (cb->s_srtt >> 2);
1847			cb->s_srtt = 0;
1848		}
1849		cb->s_snxt = cb->s_rack;
1850		/*
1851		 * If timing a packet, stop the timer.
1852		 */
1853		cb->s_rtt = 0;
1854		/*
1855		 * See very long discussion in tcp_timer.c about congestion
1856		 * window and sstrhesh
1857		 */
1858		win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2;
1859		if (win < 2)
1860			win = 2;
1861		cb->s_cwnd = CUNIT;
1862		cb->s_ssthresh = win * CUNIT;
1863		spx_output(cb, NULL);
1864		break;
1865
1866	/*
1867	 * Persistance timer into zero window.
1868	 * Force a probe to be sent.
1869	 */
1870	case SPXT_PERSIST:
1871		spxstat.spxs_persisttimeo++;
1872		spx_setpersist(cb);
1873		spx_output(cb, NULL);
1874		break;
1875
1876	/*
1877	 * Keep-alive timer went off; send something
1878	 * or drop connection if idle for too long.
1879	 */
1880	case SPXT_KEEP:
1881		spxstat.spxs_keeptimeo++;
1882		if (cb->s_state < TCPS_ESTABLISHED)
1883			goto dropit;
1884		if (cb->s_ipxpcb->ipxp_socket->so_options & SO_KEEPALIVE) {
1885		    	if (cb->s_idle >= SPXTV_MAXIDLE)
1886				goto dropit;
1887			spxstat.spxs_keepprobe++;
1888			spx_output(cb, NULL);
1889		} else
1890			cb->s_idle = 0;
1891		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
1892		break;
1893	dropit:
1894		spxstat.spxs_keepdrops++;
1895		cb = spx_drop(cb, ETIMEDOUT);
1896		break;
1897	}
1898	return (cb);
1899}
1900