spx_usrreq.c revision 139485
1170222Sdougb/*
2254402Serwin * Copyright (c) 2004 Robert N. M. Watson
3170222Sdougb * Copyright (c) 1995, Mike Mitchell
4186462Sdougb * Copyright (c) 1984, 1985, 1986, 1987, 1993
5170222Sdougb *	The Regents of the University of California.  All rights reserved.
6170222Sdougb *
7170222Sdougb * Redistribution and use in source and binary forms, with or without
8170222Sdougb * modification, are permitted provided that the following conditions
9170222Sdougb * are met:
10170222Sdougb * 1. Redistributions of source code must retain the above copyright
11170222Sdougb *    notice, this list of conditions and the following disclaimer.
12170222Sdougb * 2. Redistributions in binary form must reproduce the above copyright
13170222Sdougb *    notice, this list of conditions and the following disclaimer in the
14170222Sdougb *    documentation and/or other materials provided with the distribution.
15170222Sdougb * 3. All advertising materials mentioning features or use of this software
16170222Sdougb *    must display the following acknowledgement:
17234010Sdougb *	This product includes software developed by the University of
18170222Sdougb *	California, Berkeley and its contributors.
19170222Sdougb * 4. Neither the name of the University nor the names of its contributors
20170222Sdougb *    may be used to endorse or promote products derived from this software
21170222Sdougb *    without specific prior written permission.
22170222Sdougb *
23170222Sdougb * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24170222Sdougb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25170222Sdougb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26170222Sdougb * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27170222Sdougb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28170222Sdougb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29170222Sdougb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30254402Serwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31170222Sdougb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32170222Sdougb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33170222Sdougb * SUCH DAMAGE.
34170222Sdougb *
35170222Sdougb *	@(#)spx_usrreq.h
36170222Sdougb */
37170222Sdougb
38170222Sdougb#include <sys/cdefs.h>
39170222Sdougb__FBSDID("$FreeBSD: head/sys/netipx/spx_usrreq.c 139485 2004-12-31 17:05:37Z rwatson $");
40170222Sdougb
41170222Sdougb#include <sys/param.h>
42170222Sdougb#include <sys/lock.h>
43170222Sdougb#include <sys/malloc.h>
44170222Sdougb#include <sys/mbuf.h>
45170222Sdougb#include <sys/mutex.h>
46170222Sdougb#include <sys/proc.h>
47170222Sdougb#include <sys/protosw.h>
48170222Sdougb#include <sys/signalvar.h>
49170222Sdougb#include <sys/socket.h>
50170222Sdougb#include <sys/socketvar.h>
51170222Sdougb#include <sys/sx.h>
52170222Sdougb#include <sys/systm.h>
53170222Sdougb
54170222Sdougb#include <net/route.h>
55170222Sdougb#include <netinet/tcp_fsm.h>
56170222Sdougb
57170222Sdougb#include <netipx/ipx.h>
58170222Sdougb#include <netipx/ipx_pcb.h>
59170222Sdougb#include <netipx/ipx_var.h>
60170222Sdougb#include <netipx/spx.h>
61170222Sdougb#include <netipx/spx_debug.h>
62170222Sdougb#include <netipx/spx_timer.h>
63170222Sdougb#include <netipx/spx_var.h>
64170222Sdougb
65170222Sdougb/*
66170222Sdougb * SPX protocol implementation.
67170222Sdougb */
68170222Sdougbstatic u_short 	spx_iss;
69170222Sdougbstatic u_short	spx_newchecks[50];
70170222Sdougbstatic int	spx_hardnosed;
71170222Sdougbstatic int	spx_use_delack = 0;
72170222Sdougbstatic int	traceallspxs = 0;
73170222Sdougbstatic struct	spx 	spx_savesi;
74170222Sdougbstatic struct	spx_istat spx_istat;
75170222Sdougb
76254402Serwin/* Following was struct spxstat spxstat; */
77254402Serwin#ifndef spxstat
78170222Sdougb#define spxstat spx_istat.newstats
79254402Serwin#endif
80170222Sdougb
81170222Sdougbstatic const int spx_backoff[SPX_MAXRXTSHIFT+1] =
82170222Sdougb    { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
83170222Sdougb
84170222Sdougbstatic	struct spxpcb *spx_close(struct spxpcb *cb);
85170222Sdougbstatic	struct spxpcb *spx_disconnect(struct spxpcb *cb);
86170222Sdougbstatic	struct spxpcb *spx_drop(struct spxpcb *cb, int errno);
87170222Sdougbstatic	int spx_output(struct spxpcb *cb, struct mbuf *m0);
88170222Sdougbstatic	int spx_reass(struct spxpcb *cb, struct spx *si);
89170222Sdougbstatic	void spx_setpersist(struct spxpcb *cb);
90170222Sdougbstatic	void spx_template(struct spxpcb *cb);
91170222Sdougbstatic	struct spxpcb *spx_timers(struct spxpcb *cb, int timer);
92170222Sdougbstatic	struct spxpcb *spx_usrclosed(struct spxpcb *cb);
93170222Sdougb
94170222Sdougbstatic	int spx_usr_abort(struct socket *so);
95170222Sdougbstatic	int spx_accept(struct socket *so, struct sockaddr **nam);
96170222Sdougbstatic	int spx_attach(struct socket *so, int proto, struct thread *td);
97170222Sdougbstatic	int spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td);
98170222Sdougbstatic	int spx_connect(struct socket *so, struct sockaddr *nam,
99170222Sdougb			struct thread *td);
100170222Sdougbstatic	int spx_detach(struct socket *so);
101170222Sdougbstatic	int spx_usr_disconnect(struct socket *so);
102170222Sdougbstatic	int spx_listen(struct socket *so, struct thread *td);
103170222Sdougbstatic	int spx_rcvd(struct socket *so, int flags);
104170222Sdougbstatic	int spx_rcvoob(struct socket *so, struct mbuf *m, int flags);
105170222Sdougbstatic	int spx_send(struct socket *so, int flags, struct mbuf *m,
106170222Sdougb		     struct sockaddr *addr, struct mbuf *control,
107170222Sdougb		     struct thread *td);
108170222Sdougbstatic	int spx_shutdown(struct socket *so);
109170222Sdougbstatic	int spx_sp_attach(struct socket *so, int proto, struct thread *td);
110170222Sdougb
111170222Sdougbstruct	pr_usrreqs spx_usrreqs = {
112170222Sdougb	.pru_abort =		spx_usr_abort,
113170222Sdougb	.pru_accept =		spx_accept,
114170222Sdougb	.pru_attach =		spx_attach,
115170222Sdougb	.pru_bind =		spx_bind,
116170222Sdougb	.pru_connect =		spx_connect,
117170222Sdougb	.pru_control =		ipx_control,
118170222Sdougb	.pru_detach =		spx_detach,
119170222Sdougb	.pru_disconnect =	spx_usr_disconnect,
120170222Sdougb	.pru_listen =		spx_listen,
121170222Sdougb	.pru_peeraddr =		ipx_peeraddr,
122170222Sdougb	.pru_rcvd =		spx_rcvd,
123170222Sdougb	.pru_rcvoob =		spx_rcvoob,
124170222Sdougb	.pru_send =		spx_send,
125170222Sdougb	.pru_shutdown =		spx_shutdown,
126170222Sdougb	.pru_sockaddr =		ipx_sockaddr,
127170222Sdougb};
128170222Sdougb
129170222Sdougbstruct	pr_usrreqs spx_usrreq_sps = {
130170222Sdougb	.pru_abort =		spx_usr_abort,
131170222Sdougb	.pru_accept =		spx_accept,
132170222Sdougb	.pru_attach =		spx_sp_attach,
133170222Sdougb	.pru_bind =		spx_bind,
134170222Sdougb	.pru_connect =		spx_connect,
135170222Sdougb	.pru_control =		ipx_control,
136170222Sdougb	.pru_detach =		spx_detach,
137170222Sdougb	.pru_disconnect =	spx_usr_disconnect,
138170222Sdougb	.pru_listen =		spx_listen,
139170222Sdougb	.pru_peeraddr =		ipx_peeraddr,
140170222Sdougb	.pru_rcvd =		spx_rcvd,
141186462Sdougb	.pru_rcvoob =		spx_rcvoob,
142170222Sdougb	.pru_send =		spx_send,
143170222Sdougb	.pru_shutdown =		spx_shutdown,
144170222Sdougb	.pru_sockaddr =		ipx_sockaddr,
145170222Sdougb};
146170222Sdougb
147170222Sdougbvoid
148170222Sdougbspx_init()
149170222Sdougb{
150170222Sdougb
151170222Sdougb	spx_iss = 1; /* WRONG !! should fish it out of TODR */
152170222Sdougb}
153170222Sdougb
154170222Sdougbvoid
155170222Sdougbspx_input(m, ipxp)
156170222Sdougb	register struct mbuf *m;
157170222Sdougb	register struct ipxpcb *ipxp;
158170222Sdougb{
159170222Sdougb	register struct spxpcb *cb;
160170222Sdougb	register struct spx *si = mtod(m, struct spx *);
161170222Sdougb	register struct socket *so;
162170222Sdougb	int dropsocket = 0;
163170222Sdougb	short ostate = 0;
164170222Sdougb
165170222Sdougb	spxstat.spxs_rcvtotal++;
166170222Sdougb	if (ipxp == NULL) {
167170222Sdougb		panic("No ipxpcb in spx_input\n");
168170222Sdougb		return;
169170222Sdougb	}
170170222Sdougb
171170222Sdougb	cb = ipxtospxpcb(ipxp);
172170222Sdougb	if (cb == NULL)
173170222Sdougb		goto bad;
174170222Sdougb
175170222Sdougb	if (m->m_len < sizeof(*si)) {
176170222Sdougb		if ((m = m_pullup(m, sizeof(*si))) == NULL) {
177170222Sdougb			spxstat.spxs_rcvshort++;
178170222Sdougb			return;
179170222Sdougb		}
180170222Sdougb		si = mtod(m, struct spx *);
181170222Sdougb	}
182170222Sdougb	si->si_seq = ntohs(si->si_seq);
183170222Sdougb	si->si_ack = ntohs(si->si_ack);
184170222Sdougb	si->si_alo = ntohs(si->si_alo);
185170222Sdougb
186170222Sdougb	so = ipxp->ipxp_socket;
187170222Sdougb
188170222Sdougb	if (so->so_options & SO_DEBUG || traceallspxs) {
189170222Sdougb		ostate = cb->s_state;
190170222Sdougb		spx_savesi = *si;
191170222Sdougb	}
192170222Sdougb	if (so->so_options & SO_ACCEPTCONN) {
193170222Sdougb		struct spxpcb *ocb = cb;
194170222Sdougb
195170222Sdougb		so = sonewconn(so, 0);
196170222Sdougb		if (so == NULL) {
197170222Sdougb			goto drop;
198170222Sdougb		}
199170222Sdougb		/*
200170222Sdougb		 * This is ugly, but ....
201170222Sdougb		 *
202170222Sdougb		 * Mark socket as temporary until we're
203170222Sdougb		 * committed to keeping it.  The code at
204170222Sdougb		 * ``drop'' and ``dropwithreset'' check the
205170222Sdougb		 * flag dropsocket to see if the temporary
206170222Sdougb		 * socket created here should be discarded.
207170222Sdougb		 * We mark the socket as discardable until
208170222Sdougb		 * we're committed to it below in TCPS_LISTEN.
209170222Sdougb		 */
210170222Sdougb		dropsocket++;
211170222Sdougb		ipxp = (struct ipxpcb *)so->so_pcb;
212170222Sdougb		ipxp->ipxp_laddr = si->si_dna;
213170222Sdougb		cb = ipxtospxpcb(ipxp);
214170222Sdougb		cb->s_mtu = ocb->s_mtu;		/* preserve sockopts */
215170222Sdougb		cb->s_flags = ocb->s_flags;	/* preserve sockopts */
216170222Sdougb		cb->s_flags2 = ocb->s_flags2;	/* preserve sockopts */
217170222Sdougb		cb->s_state = TCPS_LISTEN;
218170222Sdougb	}
219170222Sdougb
220170222Sdougb	/*
221170222Sdougb	 * Packet received on connection.
222170222Sdougb	 * reset idle time and keep-alive timer;
223170222Sdougb	 */
224170222Sdougb	cb->s_idle = 0;
225170222Sdougb	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
226170222Sdougb
227170222Sdougb	switch (cb->s_state) {
228170222Sdougb
229170222Sdougb	case TCPS_LISTEN:{
230170222Sdougb		struct sockaddr_ipx *sipx, ssipx;
231170222Sdougb		struct ipx_addr laddr;
232170222Sdougb
233170222Sdougb		/*
234170222Sdougb		 * If somebody here was carying on a conversation
235170222Sdougb		 * and went away, and his pen pal thinks he can
236170222Sdougb		 * still talk, we get the misdirected packet.
237170222Sdougb		 */
238170222Sdougb		if (spx_hardnosed && (si->si_did != 0 || si->si_seq != 0)) {
239170222Sdougb			spx_istat.gonawy++;
240170222Sdougb			goto dropwithreset;
241170222Sdougb		}
242170222Sdougb		sipx = &ssipx;
243170222Sdougb		bzero(sipx, sizeof *sipx);
244170222Sdougb		sipx->sipx_len = sizeof(*sipx);
245170222Sdougb		sipx->sipx_family = AF_IPX;
246170222Sdougb		sipx->sipx_addr = si->si_sna;
247170222Sdougb		laddr = ipxp->ipxp_laddr;
248170222Sdougb		if (ipx_nullhost(laddr))
249170222Sdougb			ipxp->ipxp_laddr = si->si_dna;
250170222Sdougb		if (ipx_pcbconnect(ipxp, (struct sockaddr *)sipx, &thread0)) {
251170222Sdougb			ipxp->ipxp_laddr = laddr;
252170222Sdougb			spx_istat.noconn++;
253170222Sdougb			goto drop;
254170222Sdougb		}
255170222Sdougb		spx_template(cb);
256170222Sdougb		dropsocket = 0;		/* committed to socket */
257170222Sdougb		cb->s_did = si->si_sid;
258170222Sdougb		cb->s_rack = si->si_ack;
259170222Sdougb		cb->s_ralo = si->si_alo;
260170222Sdougb#define THREEWAYSHAKE
261170222Sdougb#ifdef THREEWAYSHAKE
262170222Sdougb		cb->s_state = TCPS_SYN_RECEIVED;
263170222Sdougb		cb->s_force = 1 + SPXT_KEEP;
264170222Sdougb		spxstat.spxs_accepts++;
265170222Sdougb		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
266170222Sdougb		}
267170222Sdougb		break;
268170222Sdougb	/*
269170222Sdougb	 * This state means that we have heard a response
270170222Sdougb	 * to our acceptance of their connection
271170222Sdougb	 * It is probably logically unnecessary in this
272170222Sdougb	 * implementation.
273170222Sdougb	 */
274170222Sdougb	 case TCPS_SYN_RECEIVED: {
275170222Sdougb		if (si->si_did != cb->s_sid) {
276170222Sdougb			spx_istat.wrncon++;
277170222Sdougb			goto drop;
278170222Sdougb		}
279170222Sdougb#endif
280170222Sdougb		ipxp->ipxp_fport =  si->si_sport;
281170222Sdougb		cb->s_timer[SPXT_REXMT] = 0;
282170222Sdougb		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
283170222Sdougb		soisconnected(so);
284170222Sdougb		cb->s_state = TCPS_ESTABLISHED;
285170222Sdougb		spxstat.spxs_accepts++;
286170222Sdougb		}
287170222Sdougb		break;
288170222Sdougb
289170222Sdougb	/*
290170222Sdougb	 * This state means that we have gotten a response
291170222Sdougb	 * to our attempt to establish a connection.
292170222Sdougb	 * We fill in the data from the other side,
293170222Sdougb	 * telling us which port to respond to, instead of the well-
294170222Sdougb	 * known one we might have sent to in the first place.
295170222Sdougb	 * We also require that this is a response to our
296170222Sdougb	 * connection id.
297170222Sdougb	 */
298170222Sdougb	case TCPS_SYN_SENT:
299170222Sdougb		if (si->si_did != cb->s_sid) {
300170222Sdougb			spx_istat.notme++;
301170222Sdougb			goto drop;
302170222Sdougb		}
303170222Sdougb		spxstat.spxs_connects++;
304170222Sdougb		cb->s_did = si->si_sid;
305170222Sdougb		cb->s_rack = si->si_ack;
306170222Sdougb		cb->s_ralo = si->si_alo;
307170222Sdougb		cb->s_dport = ipxp->ipxp_fport =  si->si_sport;
308170222Sdougb		cb->s_timer[SPXT_REXMT] = 0;
309170222Sdougb		cb->s_flags |= SF_ACKNOW;
310170222Sdougb		soisconnected(so);
311170222Sdougb		cb->s_state = TCPS_ESTABLISHED;
312170222Sdougb		/* Use roundtrip time of connection request for initial rtt */
313170222Sdougb		if (cb->s_rtt) {
314170222Sdougb			cb->s_srtt = cb->s_rtt << 3;
315170222Sdougb			cb->s_rttvar = cb->s_rtt << 1;
316170222Sdougb			SPXT_RANGESET(cb->s_rxtcur,
317170222Sdougb			    ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
318170222Sdougb			    SPXTV_MIN, SPXTV_REXMTMAX);
319170222Sdougb			    cb->s_rtt = 0;
320170222Sdougb		}
321170222Sdougb	}
322170222Sdougb	if (so->so_options & SO_DEBUG || traceallspxs)
323170222Sdougb		spx_trace(SA_INPUT, (u_char)ostate, cb, &spx_savesi, 0);
324170222Sdougb
325170222Sdougb	m->m_len -= sizeof(struct ipx);
326170222Sdougb	m->m_pkthdr.len -= sizeof(struct ipx);
327170222Sdougb	m->m_data += sizeof(struct ipx);
328170222Sdougb
329170222Sdougb	if (spx_reass(cb, si)) {
330170222Sdougb		m_freem(m);
331170222Sdougb	}
332170222Sdougb	if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT)))
333170222Sdougb		spx_output(cb, (struct mbuf *)NULL);
334170222Sdougb	cb->s_flags &= ~(SF_WIN|SF_RXT);
335170222Sdougb	return;
336170222Sdougb
337170222Sdougbdropwithreset:
338170222Sdougb	if (dropsocket) {
339170222Sdougb		struct socket *head;
340170222Sdougb		ACCEPT_LOCK();
341170222Sdougb		KASSERT((so->so_qstate & SQ_INCOMP) != 0,
342170222Sdougb		    ("spx_input: nascent socket not SQ_INCOMP on soabort()"));
343170222Sdougb		head = so->so_head;
344170222Sdougb		TAILQ_REMOVE(&head->so_incomp, so, so_list);
345170222Sdougb		head->so_incqlen--;
346170222Sdougb		so->so_qstate &= ~SQ_INCOMP;
347170222Sdougb		so->so_head = NULL;
348170222Sdougb		ACCEPT_UNLOCK();
349170222Sdougb		soabort(so);
350170222Sdougb	}
351186462Sdougb	si->si_seq = ntohs(si->si_seq);
352170222Sdougb	si->si_ack = ntohs(si->si_ack);
353170222Sdougb	si->si_alo = ntohs(si->si_alo);
354170222Sdougb	m_freem(dtom(si));
355186462Sdougb	if (cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || traceallspxs)
356170222Sdougb		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
357170222Sdougb	return;
358170222Sdougb
359170222Sdougbdrop:
360170222Sdougbbad:
361170222Sdougb	if (cb == 0 || cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG ||
362170222Sdougb            traceallspxs)
363170222Sdougb		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
364170222Sdougb	m_freem(m);
365170222Sdougb}
366170222Sdougb
367170222Sdougbstatic int spxrexmtthresh = 3;
368170222Sdougb
369170222Sdougb/*
370170222Sdougb * This is structurally similar to the tcp reassembly routine
371170222Sdougb * but its function is somewhat different:  It merely queues
372170222Sdougb * packets up, and suppresses duplicates.
373170222Sdougb */
374170222Sdougbstatic int
375170222Sdougbspx_reass(cb, si)
376170222Sdougbregister struct spxpcb *cb;
377170222Sdougbregister struct spx *si;
378170222Sdougb{
379170222Sdougb	register struct spx_q *q;
380170222Sdougb	register struct mbuf *m;
381170222Sdougb	register struct socket *so = cb->s_ipxpcb->ipxp_socket;
382170222Sdougb	char packetp = cb->s_flags & SF_HI;
383170222Sdougb	int incr;
384170222Sdougb	char wakeup = 0;
385170222Sdougb
386170222Sdougb	if (si == SI(0))
387170222Sdougb		goto present;
388170222Sdougb	/*
389170222Sdougb	 * Update our news from them.
390170222Sdougb	 */
391170222Sdougb	if (si->si_cc & SPX_SA)
392170222Sdougb		cb->s_flags |= (spx_use_delack ? SF_DELACK : SF_ACKNOW);
393170222Sdougb	if (SSEQ_GT(si->si_alo, cb->s_ralo))
394170222Sdougb		cb->s_flags |= SF_WIN;
395170222Sdougb	if (SSEQ_LEQ(si->si_ack, cb->s_rack)) {
396170222Sdougb		if ((si->si_cc & SPX_SP) && cb->s_rack != (cb->s_smax + 1)) {
397170222Sdougb			spxstat.spxs_rcvdupack++;
398170222Sdougb			/*
399170222Sdougb			 * If this is a completely duplicate ack
400170222Sdougb			 * and other conditions hold, we assume
401170222Sdougb			 * a packet has been dropped and retransmit
402170222Sdougb			 * it exactly as in tcp_input().
403170222Sdougb			 */
404170222Sdougb			if (si->si_ack != cb->s_rack ||
405170222Sdougb			    si->si_alo != cb->s_ralo)
406170222Sdougb				cb->s_dupacks = 0;
407170222Sdougb			else if (++cb->s_dupacks == spxrexmtthresh) {
408170222Sdougb				u_short onxt = cb->s_snxt;
409170222Sdougb				int cwnd = cb->s_cwnd;
410170222Sdougb
411170222Sdougb				cb->s_snxt = si->si_ack;
412170222Sdougb				cb->s_cwnd = CUNIT;
413170222Sdougb				cb->s_force = 1 + SPXT_REXMT;
414170222Sdougb				spx_output(cb, (struct mbuf *)NULL);
415170222Sdougb				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
416170222Sdougb				cb->s_rtt = 0;
417170222Sdougb				if (cwnd >= 4 * CUNIT)
418170222Sdougb					cb->s_cwnd = cwnd / 2;
419170222Sdougb				if (SSEQ_GT(onxt, cb->s_snxt))
420170222Sdougb					cb->s_snxt = onxt;
421170222Sdougb				return (1);
422170222Sdougb			}
423170222Sdougb		} else
424170222Sdougb			cb->s_dupacks = 0;
425170222Sdougb		goto update_window;
426170222Sdougb	}
427170222Sdougb	cb->s_dupacks = 0;
428170222Sdougb	/*
429170222Sdougb	 * If our correspondent acknowledges data we haven't sent
430170222Sdougb	 * TCP would drop the packet after acking.  We'll be a little
431170222Sdougb	 * more permissive
432170222Sdougb	 */
433170222Sdougb	if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) {
434170222Sdougb		spxstat.spxs_rcvacktoomuch++;
435170222Sdougb		si->si_ack = cb->s_smax + 1;
436170222Sdougb	}
437170222Sdougb	spxstat.spxs_rcvackpack++;
438170222Sdougb	/*
439170222Sdougb	 * If transmit timer is running and timed sequence
440170222Sdougb	 * number was acked, update smoothed round trip time.
441170222Sdougb	 * See discussion of algorithm in tcp_input.c
442170222Sdougb	 */
443170222Sdougb	if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) {
444170222Sdougb		spxstat.spxs_rttupdated++;
445170222Sdougb		if (cb->s_srtt != 0) {
446170222Sdougb			register short delta;
447170222Sdougb			delta = cb->s_rtt - (cb->s_srtt >> 3);
448170222Sdougb			if ((cb->s_srtt += delta) <= 0)
449170222Sdougb				cb->s_srtt = 1;
450170222Sdougb			if (delta < 0)
451170222Sdougb				delta = -delta;
452170222Sdougb			delta -= (cb->s_rttvar >> 2);
453170222Sdougb			if ((cb->s_rttvar += delta) <= 0)
454170222Sdougb				cb->s_rttvar = 1;
455170222Sdougb		} else {
456170222Sdougb			/*
457170222Sdougb			 * No rtt measurement yet
458170222Sdougb			 */
459170222Sdougb			cb->s_srtt = cb->s_rtt << 3;
460170222Sdougb			cb->s_rttvar = cb->s_rtt << 1;
461170222Sdougb		}
462170222Sdougb		cb->s_rtt = 0;
463170222Sdougb		cb->s_rxtshift = 0;
464170222Sdougb		SPXT_RANGESET(cb->s_rxtcur,
465170222Sdougb			((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
466170222Sdougb			SPXTV_MIN, SPXTV_REXMTMAX);
467170222Sdougb	}
468170222Sdougb	/*
469170222Sdougb	 * If all outstanding data is acked, stop retransmit
470170222Sdougb	 * timer and remember to restart (more output or persist).
471170222Sdougb	 * If there is more data to be acked, restart retransmit
472170222Sdougb	 * timer, using current (possibly backed-off) value;
473170222Sdougb	 */
474170222Sdougb	if (si->si_ack == cb->s_smax + 1) {
475170222Sdougb		cb->s_timer[SPXT_REXMT] = 0;
476170222Sdougb		cb->s_flags |= SF_RXT;
477170222Sdougb	} else if (cb->s_timer[SPXT_PERSIST] == 0)
478170222Sdougb		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
479170222Sdougb	/*
480170222Sdougb	 * When new data is acked, open the congestion window.
481170222Sdougb	 * If the window gives us less than ssthresh packets
482170222Sdougb	 * in flight, open exponentially (maxseg at a time).
483170222Sdougb	 * Otherwise open linearly (maxseg^2 / cwnd at a time).
484170222Sdougb	 */
485170222Sdougb	incr = CUNIT;
486170222Sdougb	if (cb->s_cwnd > cb->s_ssthresh)
487170222Sdougb		incr = max(incr * incr / cb->s_cwnd, 1);
488170222Sdougb	cb->s_cwnd = min(cb->s_cwnd + incr, cb->s_cwmx);
489170222Sdougb	/*
490170222Sdougb	 * Trim Acked data from output queue.
491170222Sdougb	 */
492170222Sdougb	while ((m = so->so_snd.sb_mb) != NULL) {
493170222Sdougb		if (SSEQ_LT((mtod(m, struct spx *))->si_seq, si->si_ack))
494170222Sdougb			sbdroprecord(&so->so_snd);
495170222Sdougb		else
496170222Sdougb			break;
497170222Sdougb	}
498170222Sdougb	sowwakeup(so);
499170222Sdougb	cb->s_rack = si->si_ack;
500170222Sdougbupdate_window:
501170222Sdougb	if (SSEQ_LT(cb->s_snxt, cb->s_rack))
502170222Sdougb		cb->s_snxt = cb->s_rack;
503170222Sdougb	if (SSEQ_LT(cb->s_swl1, si->si_seq) || ((cb->s_swl1 == si->si_seq &&
504170222Sdougb	    (SSEQ_LT(cb->s_swl2, si->si_ack))) ||
505170222Sdougb	     (cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo)))) {
506170222Sdougb		/* keep track of pure window updates */
507170222Sdougb		if ((si->si_cc & SPX_SP) && cb->s_swl2 == si->si_ack
508170222Sdougb		    && SSEQ_LT(cb->s_ralo, si->si_alo)) {
509170222Sdougb			spxstat.spxs_rcvwinupd++;
510170222Sdougb			spxstat.spxs_rcvdupack--;
511170222Sdougb		}
512170222Sdougb		cb->s_ralo = si->si_alo;
513170222Sdougb		cb->s_swl1 = si->si_seq;
514170222Sdougb		cb->s_swl2 = si->si_ack;
515170222Sdougb		cb->s_swnd = (1 + si->si_alo - si->si_ack);
516170222Sdougb		if (cb->s_swnd > cb->s_smxw)
517186462Sdougb			cb->s_smxw = cb->s_swnd;
518170222Sdougb		cb->s_flags |= SF_WIN;
519170222Sdougb	}
520170222Sdougb	/*
521170222Sdougb	 * If this packet number is higher than that which
522170222Sdougb	 * we have allocated refuse it, unless urgent
523170222Sdougb	 */
524170222Sdougb	if (SSEQ_GT(si->si_seq, cb->s_alo)) {
525170222Sdougb		if (si->si_cc & SPX_SP) {
526170222Sdougb			spxstat.spxs_rcvwinprobe++;
527170222Sdougb			return (1);
528170222Sdougb		} else
529170222Sdougb			spxstat.spxs_rcvpackafterwin++;
530170222Sdougb		if (si->si_cc & SPX_OB) {
531170222Sdougb			if (SSEQ_GT(si->si_seq, cb->s_alo + 60)) {
532170222Sdougb				m_freem(dtom(si));
533170222Sdougb				return (0);
534170222Sdougb			} /* else queue this packet; */
535170222Sdougb		} else {
536170222Sdougb			/*register struct socket *so = cb->s_ipxpcb->ipxp_socket;
537170222Sdougb			if (so->so_state && SS_NOFDREF) {
538170222Sdougb				spx_close(cb);
539170222Sdougb			} else
540170222Sdougb				       would crash system*/
541170222Sdougb			spx_istat.notyet++;
542170222Sdougb			m_freem(dtom(si));
543170222Sdougb			return (0);
544170222Sdougb		}
545170222Sdougb	}
546170222Sdougb	/*
547170222Sdougb	 * If this is a system packet, we don't need to
548170222Sdougb	 * queue it up, and won't update acknowledge #
549170222Sdougb	 */
550170222Sdougb	if (si->si_cc & SPX_SP) {
551170222Sdougb		return (1);
552170222Sdougb	}
553170222Sdougb	/*
554170222Sdougb	 * We have already seen this packet, so drop.
555170222Sdougb	 */
556170222Sdougb	if (SSEQ_LT(si->si_seq, cb->s_ack)) {
557170222Sdougb		spx_istat.bdreas++;
558170222Sdougb		spxstat.spxs_rcvduppack++;
559170222Sdougb		if (si->si_seq == cb->s_ack - 1)
560170222Sdougb			spx_istat.lstdup++;
561170222Sdougb		return (1);
562186462Sdougb	}
563170222Sdougb	/*
564170222Sdougb	 * Loop through all packets queued up to insert in
565170222Sdougb	 * appropriate sequence.
566170222Sdougb	 */
567170222Sdougb	for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) {
568170222Sdougb		if (si->si_seq == SI(q)->si_seq) {
569170222Sdougb			spxstat.spxs_rcvduppack++;
570170222Sdougb			return (1);
571170222Sdougb		}
572170222Sdougb		if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) {
573170222Sdougb			spxstat.spxs_rcvoopack++;
574170222Sdougb			break;
575170222Sdougb		}
576170222Sdougb	}
577170222Sdougb	insque(si, q->si_prev);
578170222Sdougb	/*
579170222Sdougb	 * If this packet is urgent, inform process
580170222Sdougb	 */
581170222Sdougb	if (si->si_cc & SPX_OB) {
582170222Sdougb		cb->s_iobc = ((char *)si)[1 + sizeof(*si)];
583170222Sdougb		sohasoutofband(so);
584170222Sdougb		cb->s_oobflags |= SF_IOOB;
585170222Sdougb	}
586170222Sdougbpresent:
587170222Sdougb#define SPINC sizeof(struct spxhdr)
588170222Sdougb	/*
589170222Sdougb	 * Loop through all packets queued up to update acknowledge
590170222Sdougb	 * number, and present all acknowledged data to user;
591170222Sdougb	 * If in packet interface mode, show packet headers.
592170222Sdougb	 */
593170222Sdougb	for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) {
594170222Sdougb		  if (SI(q)->si_seq == cb->s_ack) {
595170222Sdougb			cb->s_ack++;
596170222Sdougb			m = dtom(q);
597170222Sdougb			if (SI(q)->si_cc & SPX_OB) {
598170222Sdougb				cb->s_oobflags &= ~SF_IOOB;
599170222Sdougb				SOCKBUF_LOCK(&so->so_rcv);
600170222Sdougb				if (so->so_rcv.sb_cc)
601170222Sdougb					so->so_oobmark = so->so_rcv.sb_cc;
602170222Sdougb				else
603170222Sdougb					so->so_rcv.sb_state |= SBS_RCVATMARK;
604170222Sdougb				SOCKBUF_UNLOCK(&so->so_rcv);
605170222Sdougb			}
606170222Sdougb			q = q->si_prev;
607170222Sdougb			remque(q->si_next);
608170222Sdougb			wakeup = 1;
609170222Sdougb			spxstat.spxs_rcvpack++;
610170222Sdougb#ifdef SF_NEWCALL
611170222Sdougb			if (cb->s_flags2 & SF_NEWCALL) {
612170222Sdougb				struct spxhdr *sp = mtod(m, struct spxhdr *);
613170222Sdougb				u_char dt = sp->spx_dt;
614170222Sdougb				spx_newchecks[4]++;
615170222Sdougb				if (dt != cb->s_rhdr.spx_dt) {
616170222Sdougb					struct mbuf *mm =
617170222Sdougb					   m_getclr(M_DONTWAIT, MT_CONTROL);
618170222Sdougb					spx_newchecks[0]++;
619170222Sdougb					if (mm != NULL) {
620170222Sdougb						u_short *s =
621170222Sdougb							mtod(mm, u_short *);
622170222Sdougb						cb->s_rhdr.spx_dt = dt;
623170222Sdougb						mm->m_len = 5; /*XXX*/
624170222Sdougb						s[0] = 5;
625170222Sdougb						s[1] = 1;
626170222Sdougb						*(u_char *)(&s[2]) = dt;
627170222Sdougb						sbappend(&so->so_rcv, mm);
628170222Sdougb					}
629170222Sdougb				}
630170222Sdougb				if (sp->spx_cc & SPX_OB) {
631170222Sdougb					MCHTYPE(m, MT_OOBDATA);
632170222Sdougb					spx_newchecks[1]++;
633170222Sdougb					SOCKBUF_LOCK(&so->so_rcv);
634170222Sdougb					so->so_oobmark = 0;
635170222Sdougb					so->so_rcv.sb_state &= ~SBS_RCVATMARK;
636170222Sdougb					SOCKBUF_UNLOCK(&so->so_rcv);
637170222Sdougb				}
638170222Sdougb				if (packetp == 0) {
639170222Sdougb					m->m_data += SPINC;
640170222Sdougb					m->m_len -= SPINC;
641170222Sdougb					m->m_pkthdr.len -= SPINC;
642170222Sdougb				}
643170222Sdougb				if ((sp->spx_cc & SPX_EM) || packetp) {
644170222Sdougb					sbappendrecord(&so->so_rcv, m);
645170222Sdougb					spx_newchecks[9]++;
646170222Sdougb				} else
647170222Sdougb					sbappend(&so->so_rcv, m);
648170222Sdougb			} else
649170222Sdougb#endif
650170222Sdougb			if (packetp) {
651170222Sdougb				sbappendrecord(&so->so_rcv, m);
652170222Sdougb			} else {
653170222Sdougb				cb->s_rhdr = *mtod(m, struct spxhdr *);
654170222Sdougb				m->m_data += SPINC;
655170222Sdougb				m->m_len -= SPINC;
656170222Sdougb				m->m_pkthdr.len -= SPINC;
657170222Sdougb				sbappend(&so->so_rcv, m);
658170222Sdougb			}
659170222Sdougb		  } else
660170222Sdougb			break;
661170222Sdougb	}
662170222Sdougb	if (wakeup)
663170222Sdougb		sorwakeup(so);
664170222Sdougb	return (0);
665170222Sdougb}
666170222Sdougb
667170222Sdougbvoid
668170222Sdougbspx_ctlinput(cmd, arg_as_sa, dummy)
669170222Sdougb	int cmd;
670170222Sdougb	struct sockaddr *arg_as_sa;	/* XXX should be swapped with dummy */
671170222Sdougb	void *dummy;
672170222Sdougb{
673170222Sdougb	caddr_t arg = (/* XXX */ caddr_t)arg_as_sa;
674170222Sdougb	struct ipx_addr *na;
675170222Sdougb	struct sockaddr_ipx *sipx;
676170222Sdougb
677170222Sdougb	if (cmd < 0 || cmd >= PRC_NCMDS)
678170222Sdougb		return;
679170222Sdougb
680170222Sdougb	switch (cmd) {
681186462Sdougb
682170222Sdougb	case PRC_ROUTEDEAD:
683170222Sdougb		return;
684170222Sdougb
685170222Sdougb	case PRC_IFDOWN:
686170222Sdougb	case PRC_HOSTDEAD:
687170222Sdougb	case PRC_HOSTUNREACH:
688170222Sdougb		sipx = (struct sockaddr_ipx *)arg;
689170222Sdougb		if (sipx->sipx_family != AF_IPX)
690170222Sdougb			return;
691170222Sdougb		na = &sipx->sipx_addr;
692170222Sdougb		break;
693170222Sdougb
694170222Sdougb	default:
695170222Sdougb		break;
696170222Sdougb	}
697170222Sdougb}
698170222Sdougb
699170222Sdougbstatic int
700170222Sdougbspx_output(cb, m0)
701170222Sdougb	register struct spxpcb *cb;
702170222Sdougb	struct mbuf *m0;
703170222Sdougb{
704170222Sdougb	struct socket *so = cb->s_ipxpcb->ipxp_socket;
705186462Sdougb	register struct mbuf *m;
706170222Sdougb	register struct spx *si = (struct spx *)NULL;
707170222Sdougb	register struct sockbuf *sb = &so->so_snd;
708170222Sdougb	int len = 0, win, rcv_win;
709170222Sdougb	short span, off, recordp = 0;
710170222Sdougb	u_short alo;
711170222Sdougb	int error = 0, sendalot;
712170222Sdougb#ifdef notdef
713170222Sdougb	int idle;
714170222Sdougb#endif
715170222Sdougb	struct mbuf *mprev;
716170222Sdougb
717170222Sdougb	if (m0 != NULL) {
718170222Sdougb		int mtu = cb->s_mtu;
719170222Sdougb		int datalen;
720170222Sdougb		/*
721170222Sdougb		 * Make sure that packet isn't too big.
722170222Sdougb		 */
723170222Sdougb		for (m = m0; m != NULL; m = m->m_next) {
724170222Sdougb			mprev = m;
725170222Sdougb			len += m->m_len;
726170222Sdougb			if (m->m_flags & M_EOR)
727170222Sdougb				recordp = 1;
728170222Sdougb		}
729170222Sdougb		datalen = (cb->s_flags & SF_HO) ?
730170222Sdougb				len - sizeof(struct spxhdr) : len;
731170222Sdougb		if (datalen > mtu) {
732170222Sdougb			if (cb->s_flags & SF_PI) {
733170222Sdougb				m_freem(m0);
734170222Sdougb				return (EMSGSIZE);
735170222Sdougb			} else {
736170222Sdougb				int oldEM = cb->s_cc & SPX_EM;
737170222Sdougb
738170222Sdougb				cb->s_cc &= ~SPX_EM;
739170222Sdougb				while (len > mtu) {
740170222Sdougb					/*
741170222Sdougb					 * Here we are only being called
742170222Sdougb					 * from usrreq(), so it is OK to
743170222Sdougb					 * block.
744170222Sdougb					 */
745170222Sdougb					m = m_copym(m0, 0, mtu, M_TRYWAIT);
746170222Sdougb					if (cb->s_flags & SF_NEWCALL) {
747170222Sdougb					    struct mbuf *mm = m;
748170222Sdougb					    spx_newchecks[7]++;
749170222Sdougb					    while (mm != NULL) {
750170222Sdougb						mm->m_flags &= ~M_EOR;
751170222Sdougb						mm = mm->m_next;
752170222Sdougb					    }
753170222Sdougb					}
754170222Sdougb					error = spx_output(cb, m);
755170222Sdougb					if (error) {
756170222Sdougb						cb->s_cc |= oldEM;
757170222Sdougb						m_freem(m0);
758170222Sdougb						return (error);
759170222Sdougb					}
760170222Sdougb					m_adj(m0, mtu);
761170222Sdougb					len -= mtu;
762170222Sdougb				}
763170222Sdougb				cb->s_cc |= oldEM;
764170222Sdougb			}
765170222Sdougb		}
766170222Sdougb		/*
767170222Sdougb		 * Force length even, by adding a "garbage byte" if
768170222Sdougb		 * necessary.
769170222Sdougb		 */
770170222Sdougb		if (len & 1) {
771170222Sdougb			m = mprev;
772170222Sdougb			if (M_TRAILINGSPACE(m) >= 1)
773170222Sdougb				m->m_len++;
774170222Sdougb			else {
775170222Sdougb				struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
776170222Sdougb
777170222Sdougb				if (m1 == NULL) {
778170222Sdougb					m_freem(m0);
779170222Sdougb					return (ENOBUFS);
780254402Serwin				}
781170222Sdougb				m1->m_len = 1;
782170222Sdougb				*(mtod(m1, u_char *)) = 0;
783254402Serwin				m->m_next = m1;
784254402Serwin			}
785254402Serwin		}
786254402Serwin		m = m_gethdr(M_DONTWAIT, MT_HEADER);
787254402Serwin		if (m == NULL) {
788170222Sdougb			m_freem(m0);
789170222Sdougb			return (ENOBUFS);
790170222Sdougb		}
791170222Sdougb		/*
792170222Sdougb		 * Fill in mbuf with extended SP header
793170222Sdougb		 * and addresses and length put into network format.
794170222Sdougb		 */
795170222Sdougb		MH_ALIGN(m, sizeof(struct spx));
796170222Sdougb		m->m_len = sizeof(struct spx);
797170222Sdougb		m->m_next = m0;
798170222Sdougb		si = mtod(m, struct spx *);
799170222Sdougb		si->si_i = *cb->s_ipx;
800170222Sdougb		si->si_s = cb->s_shdr;
801170222Sdougb		if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) {
802170222Sdougb			register struct spxhdr *sh;
803170222Sdougb			if (m0->m_len < sizeof(*sh)) {
804170222Sdougb				if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) {
805170222Sdougb					m_free(m);
806170222Sdougb					m_freem(m0);
807170222Sdougb					return (EINVAL);
808170222Sdougb				}
809170222Sdougb				m->m_next = m0;
810170222Sdougb			}
811170222Sdougb			sh = mtod(m0, struct spxhdr *);
812170222Sdougb			si->si_dt = sh->spx_dt;
813170222Sdougb			si->si_cc |= sh->spx_cc & SPX_EM;
814170222Sdougb			m0->m_len -= sizeof(*sh);
815170222Sdougb			m0->m_data += sizeof(*sh);
816170222Sdougb			len -= sizeof(*sh);
817170222Sdougb		}
818170222Sdougb		len += sizeof(*si);
819170222Sdougb		if ((cb->s_flags2 & SF_NEWCALL) && recordp) {
820170222Sdougb			si->si_cc |= SPX_EM;
821170222Sdougb			spx_newchecks[8]++;
822170222Sdougb		}
823170222Sdougb		if (cb->s_oobflags & SF_SOOB) {
824170222Sdougb			/*
825170222Sdougb			 * Per jqj@cornell:
826170222Sdougb			 * make sure OB packets convey exactly 1 byte.
827170222Sdougb			 * If the packet is 1 byte or larger, we
828170222Sdougb			 * have already guaranted there to be at least
829170222Sdougb			 * one garbage byte for the checksum, and
830170222Sdougb			 * extra bytes shouldn't hurt!
831170222Sdougb			 */
832170222Sdougb			if (len > sizeof(*si)) {
833254402Serwin				si->si_cc |= SPX_OB;
834254402Serwin				len = (1 + sizeof(*si));
835254402Serwin			}
836254402Serwin		}
837170222Sdougb		si->si_len = htons((u_short)len);
838170222Sdougb		m->m_pkthdr.len = ((len - 1) | 1) + 1;
839170222Sdougb		/*
840170222Sdougb		 * queue stuff up for output
841170222Sdougb		 */
842170222Sdougb		sbappendrecord(sb, m);
843170222Sdougb		cb->s_seq++;
844170222Sdougb	}
845170222Sdougb#ifdef notdef
846170222Sdougb	idle = (cb->s_smax == (cb->s_rack - 1));
847170222Sdougb#endif
848170222Sdougbagain:
849170222Sdougb	sendalot = 0;
850170222Sdougb	off = cb->s_snxt - cb->s_rack;
851170222Sdougb	win = min(cb->s_swnd, (cb->s_cwnd / CUNIT));
852170222Sdougb
853170222Sdougb	/*
854170222Sdougb	 * If in persist timeout with window of 0, send a probe.
855170222Sdougb	 * Otherwise, if window is small but nonzero
856170222Sdougb	 * and timer expired, send what we can and go into
857170222Sdougb	 * transmit state.
858170222Sdougb	 */
859170222Sdougb	if (cb->s_force == 1 + SPXT_PERSIST) {
860170222Sdougb		if (win != 0) {
861170222Sdougb			cb->s_timer[SPXT_PERSIST] = 0;
862170222Sdougb			cb->s_rxtshift = 0;
863170222Sdougb		}
864170222Sdougb	}
865170222Sdougb	span = cb->s_seq - cb->s_rack;
866170222Sdougb	len = min(span, win) - off;
867170222Sdougb
868170222Sdougb	if (len < 0) {
869170222Sdougb		/*
870170222Sdougb		 * Window shrank after we went into it.
871254402Serwin		 * If window shrank to 0, cancel pending
872254402Serwin		 * restransmission and pull s_snxt back
873254402Serwin		 * to (closed) window.  We will enter persist
874254402Serwin		 * state below.  If the widndow didn't close completely,
875254402Serwin		 * just wait for an ACK.
876170222Sdougb		 */
877170222Sdougb		len = 0;
878170222Sdougb		if (win == 0) {
879170222Sdougb			cb->s_timer[SPXT_REXMT] = 0;
880170222Sdougb			cb->s_snxt = cb->s_rack;
881170222Sdougb		}
882170222Sdougb	}
883170222Sdougb	if (len > 1)
884254402Serwin		sendalot = 1;
885170222Sdougb	rcv_win = sbspace(&so->so_rcv);
886170222Sdougb
887170222Sdougb	/*
888170222Sdougb	 * Send if we owe peer an ACK.
889170222Sdougb	 */
890170222Sdougb	if (cb->s_oobflags & SF_SOOB) {
891170222Sdougb		/*
892170222Sdougb		 * must transmit this out of band packet
893170222Sdougb		 */
894170222Sdougb		cb->s_oobflags &= ~ SF_SOOB;
895170222Sdougb		sendalot = 1;
896170222Sdougb		spxstat.spxs_sndurg++;
897170222Sdougb		goto found;
898170222Sdougb	}
899170222Sdougb	if (cb->s_flags & SF_ACKNOW)
900170222Sdougb		goto send;
901170222Sdougb	if (cb->s_state < TCPS_ESTABLISHED)
902170222Sdougb		goto send;
903254402Serwin	/*
904170222Sdougb	 * Silly window can't happen in spx.
905170222Sdougb	 * Code from tcp deleted.
906170222Sdougb	 */
907170222Sdougb	if (len)
908170222Sdougb		goto send;
909170222Sdougb	/*
910170222Sdougb	 * Compare available window to amount of window
911170222Sdougb	 * known to peer (as advertised window less
912170222Sdougb	 * next expected input.)  If the difference is at least two
913170222Sdougb	 * packets or at least 35% of the mximum possible window,
914170222Sdougb	 * then want to send a window update to peer.
915170222Sdougb	 */
916170222Sdougb	if (rcv_win > 0) {
917170222Sdougb		u_short delta =  1 + cb->s_alo - cb->s_ack;
918170222Sdougb		int adv = rcv_win - (delta * cb->s_mtu);
919170222Sdougb
920170222Sdougb		if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) ||
921170222Sdougb		    (100 * adv / so->so_rcv.sb_hiwat >= 35)) {
922170222Sdougb			spxstat.spxs_sndwinup++;
923170222Sdougb			cb->s_flags |= SF_ACKNOW;
924170222Sdougb			goto send;
925170222Sdougb		}
926170222Sdougb
927170222Sdougb	}
928186462Sdougb	/*
929170222Sdougb	 * Many comments from tcp_output.c are appropriate here
930170222Sdougb	 * including . . .
931170222Sdougb	 * If send window is too small, there is data to transmit, and no
932170222Sdougb	 * retransmit or persist is pending, then go to persist state.
933170222Sdougb	 * If nothing happens soon, send when timer expires:
934170222Sdougb	 * if window is nonzero, transmit what we can,
935170222Sdougb	 * otherwise send a probe.
936170222Sdougb	 */
937170222Sdougb	if (so->so_snd.sb_cc && cb->s_timer[SPXT_REXMT] == 0 &&
938170222Sdougb		cb->s_timer[SPXT_PERSIST] == 0) {
939170222Sdougb			cb->s_rxtshift = 0;
940170222Sdougb			spx_setpersist(cb);
941170222Sdougb	}
942170222Sdougb	/*
943170222Sdougb	 * No reason to send a packet, just return.
944170222Sdougb	 */
945170222Sdougb	cb->s_outx = 1;
946170222Sdougb	return (0);
947170222Sdougb
948170222Sdougbsend:
949170222Sdougb	/*
950170222Sdougb	 * Find requested packet.
951170222Sdougb	 */
952170222Sdougb	si = 0;
953170222Sdougb	if (len > 0) {
954170222Sdougb		cb->s_want = cb->s_snxt;
955170222Sdougb		for (m = sb->sb_mb; m != NULL; m = m->m_act) {
956170222Sdougb			si = mtod(m, struct spx *);
957170222Sdougb			if (SSEQ_LEQ(cb->s_snxt, si->si_seq))
958170222Sdougb				break;
959170222Sdougb		}
960170222Sdougb	found:
961170222Sdougb		if (si != NULL) {
962170222Sdougb			if (si->si_seq == cb->s_snxt)
963170222Sdougb					cb->s_snxt++;
964170222Sdougb				else
965170222Sdougb					spxstat.spxs_sndvoid++, si = 0;
966170222Sdougb		}
967170222Sdougb	}
968170222Sdougb	/*
969170222Sdougb	 * update window
970170222Sdougb	 */
971170222Sdougb	if (rcv_win < 0)
972170222Sdougb		rcv_win = 0;
973170222Sdougb	alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu));
974170222Sdougb	if (SSEQ_LT(alo, cb->s_alo))
975170222Sdougb		alo = cb->s_alo;
976170222Sdougb
977170222Sdougb	if (si != NULL) {
978170222Sdougb		/*
979170222Sdougb		 * must make a copy of this packet for
980186462Sdougb		 * ipx_output to monkey with
981186462Sdougb		 */
982170222Sdougb		m = m_copy(dtom(si), 0, (int)M_COPYALL);
983186462Sdougb		if (m == NULL) {
984186462Sdougb			return (ENOBUFS);
985186462Sdougb		}
986186462Sdougb		si = mtod(m, struct spx *);
987186462Sdougb		if (SSEQ_LT(si->si_seq, cb->s_smax))
988170222Sdougb			spxstat.spxs_sndrexmitpack++;
989170222Sdougb		else
990170222Sdougb			spxstat.spxs_sndpack++;
991170222Sdougb	} else if (cb->s_force || cb->s_flags & SF_ACKNOW) {
992170222Sdougb		/*
993170222Sdougb		 * Must send an acknowledgement or a probe
994170222Sdougb		 */
995170222Sdougb		if (cb->s_force)
996170222Sdougb			spxstat.spxs_sndprobe++;
997170222Sdougb		if (cb->s_flags & SF_ACKNOW)
998170222Sdougb			spxstat.spxs_sndacks++;
999170222Sdougb		m = m_gethdr(M_DONTWAIT, MT_HEADER);
1000170222Sdougb		if (m == NULL)
1001170222Sdougb			return (ENOBUFS);
1002170222Sdougb		/*
1003170222Sdougb		 * Fill in mbuf with extended SP header
1004170222Sdougb		 * and addresses and length put into network format.
1005170222Sdougb		 */
1006170222Sdougb		MH_ALIGN(m, sizeof(struct spx));
1007170222Sdougb		m->m_len = sizeof(*si);
1008170222Sdougb		m->m_pkthdr.len = sizeof(*si);
1009170222Sdougb		si = mtod(m, struct spx *);
1010170222Sdougb		si->si_i = *cb->s_ipx;
1011170222Sdougb		si->si_s = cb->s_shdr;
1012170222Sdougb		si->si_seq = cb->s_smax + 1;
1013170222Sdougb		si->si_len = htons(sizeof(*si));
1014170222Sdougb		si->si_cc |= SPX_SP;
1015170222Sdougb	} else {
1016170222Sdougb		cb->s_outx = 3;
1017170222Sdougb		if (so->so_options & SO_DEBUG || traceallspxs)
1018170222Sdougb			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
1019170222Sdougb		return (0);
1020170222Sdougb	}
1021170222Sdougb	/*
1022170222Sdougb	 * Stuff checksum and output datagram.
1023170222Sdougb	 */
1024170222Sdougb	if ((si->si_cc & SPX_SP) == 0) {
1025170222Sdougb		if (cb->s_force != (1 + SPXT_PERSIST) ||
1026170222Sdougb		    cb->s_timer[SPXT_PERSIST] == 0) {
1027170222Sdougb			/*
1028170222Sdougb			 * If this is a new packet and we are not currently
1029170222Sdougb			 * timing anything, time this one.
1030170222Sdougb			 */
1031170222Sdougb			if (SSEQ_LT(cb->s_smax, si->si_seq)) {
1032170222Sdougb				cb->s_smax = si->si_seq;
1033170222Sdougb				if (cb->s_rtt == 0) {
1034170222Sdougb					spxstat.spxs_segstimed++;
1035170222Sdougb					cb->s_rtseq = si->si_seq;
1036170222Sdougb					cb->s_rtt = 1;
1037170222Sdougb				}
1038170222Sdougb			}
1039170222Sdougb			/*
1040170222Sdougb			 * Set rexmt timer if not currently set,
1041170222Sdougb			 * Initial value for retransmit timer is smoothed
1042170222Sdougb			 * round-trip time + 2 * round-trip time variance.
1043170222Sdougb			 * Initialize shift counter which is used for backoff
1044170222Sdougb			 * of retransmit time.
1045170222Sdougb			 */
1046170222Sdougb			if (cb->s_timer[SPXT_REXMT] == 0 &&
1047170222Sdougb			    cb->s_snxt != cb->s_rack) {
1048170222Sdougb				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1049170222Sdougb				if (cb->s_timer[SPXT_PERSIST]) {
1050170222Sdougb					cb->s_timer[SPXT_PERSIST] = 0;
1051170222Sdougb					cb->s_rxtshift = 0;
1052170222Sdougb				}
1053170222Sdougb			}
1054170222Sdougb		} else if (SSEQ_LT(cb->s_smax, si->si_seq)) {
1055170222Sdougb			cb->s_smax = si->si_seq;
1056170222Sdougb		}
1057170222Sdougb	} else if (cb->s_state < TCPS_ESTABLISHED) {
1058170222Sdougb		if (cb->s_rtt == 0)
1059170222Sdougb			cb->s_rtt = 1; /* Time initial handshake */
1060170222Sdougb		if (cb->s_timer[SPXT_REXMT] == 0)
1061170222Sdougb			cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1062170222Sdougb	}
1063170222Sdougb	{
1064170222Sdougb		/*
1065170222Sdougb		 * Do not request acks when we ack their data packets or
1066170222Sdougb		 * when we do a gratuitous window update.
1067170222Sdougb		 */
1068170222Sdougb		if (((si->si_cc & SPX_SP) == 0) || cb->s_force)
1069170222Sdougb				si->si_cc |= SPX_SA;
1070170222Sdougb		si->si_seq = htons(si->si_seq);
1071170222Sdougb		si->si_alo = htons(alo);
1072170222Sdougb		si->si_ack = htons(cb->s_ack);
1073170222Sdougb
1074170222Sdougb		if (ipxcksum) {
1075170222Sdougb			si->si_sum = ipx_cksum(m, ntohs(si->si_len));
1076170222Sdougb		} else
1077170222Sdougb			si->si_sum = 0xffff;
1078170222Sdougb
1079170222Sdougb		cb->s_outx = 4;
1080170222Sdougb		if (so->so_options & SO_DEBUG || traceallspxs)
1081170222Sdougb			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
1082170222Sdougb
1083170222Sdougb		if (so->so_options & SO_DONTROUTE)
1084170222Sdougb			error = ipx_outputfl(m, (struct route *)NULL, IPX_ROUTETOIF);
1085170222Sdougb		else
1086170222Sdougb			error = ipx_outputfl(m, &cb->s_ipxpcb->ipxp_route, 0);
1087170222Sdougb	}
1088170222Sdougb	if (error) {
1089170222Sdougb		return (error);
1090170222Sdougb	}
1091170222Sdougb	spxstat.spxs_sndtotal++;
1092170222Sdougb	/*
1093170222Sdougb	 * Data sent (as far as we can tell).
1094170222Sdougb	 * If this advertises a larger window than any other segment,
1095170222Sdougb	 * then remember the size of the advertized window.
1096170222Sdougb	 * Any pending ACK has now been sent.
1097170222Sdougb	 */
1098170222Sdougb	cb->s_force = 0;
1099170222Sdougb	cb->s_flags &= ~(SF_ACKNOW|SF_DELACK);
1100170222Sdougb	if (SSEQ_GT(alo, cb->s_alo))
1101170222Sdougb		cb->s_alo = alo;
1102170222Sdougb	if (sendalot)
1103170222Sdougb		goto again;
1104170222Sdougb	cb->s_outx = 5;
1105170222Sdougb	return (0);
1106170222Sdougb}
1107170222Sdougb
1108170222Sdougbstatic int spx_do_persist_panics = 0;
1109170222Sdougb
1110170222Sdougbstatic void
1111170222Sdougbspx_setpersist(cb)
1112170222Sdougb	register struct spxpcb *cb;
1113170222Sdougb{
1114170222Sdougb	register int t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
1115170222Sdougb
1116170222Sdougb	if (cb->s_timer[SPXT_REXMT] && spx_do_persist_panics)
1117170222Sdougb		panic("spx_output REXMT");
1118170222Sdougb	/*
1119170222Sdougb	 * Start/restart persistance timer.
1120170222Sdougb	 */
1121186462Sdougb	SPXT_RANGESET(cb->s_timer[SPXT_PERSIST],
1122170222Sdougb	    t*spx_backoff[cb->s_rxtshift],
1123170222Sdougb	    SPXTV_PERSMIN, SPXTV_PERSMAX);
1124170222Sdougb	if (cb->s_rxtshift < SPX_MAXRXTSHIFT)
1125170222Sdougb		cb->s_rxtshift++;
1126170222Sdougb}
1127170222Sdougb
1128170222Sdougbint
1129170222Sdougbspx_ctloutput(so, sopt)
1130170222Sdougb	struct socket *so;
1131170222Sdougb	struct sockopt *sopt;
1132170222Sdougb{
1133170222Sdougb	struct ipxpcb *ipxp = sotoipxpcb(so);
1134170222Sdougb	register struct spxpcb *cb;
1135170222Sdougb	int mask, error;
1136170222Sdougb	short soptval;
1137170222Sdougb	u_short usoptval;
1138170222Sdougb	int optval;
1139170222Sdougb
1140170222Sdougb	error = 0;
1141170222Sdougb
1142170222Sdougb	if (sopt->sopt_level != IPXPROTO_SPX) {
1143170222Sdougb		/* This will have to be changed when we do more general
1144170222Sdougb		   stacking of protocols */
1145170222Sdougb		return (ipx_ctloutput(so, sopt));
1146170222Sdougb	}
1147170222Sdougb	if (ipxp == NULL)
1148170222Sdougb		return (EINVAL);
1149170222Sdougb	else
1150170222Sdougb		cb = ipxtospxpcb(ipxp);
1151170222Sdougb
1152170222Sdougb	switch (sopt->sopt_dir) {
1153170222Sdougb	case SOPT_GET:
1154170222Sdougb		switch (sopt->sopt_name) {
1155170222Sdougb		case SO_HEADERS_ON_INPUT:
1156170222Sdougb			mask = SF_HI;
1157170222Sdougb			goto get_flags;
1158170222Sdougb
1159170222Sdougb		case SO_HEADERS_ON_OUTPUT:
1160170222Sdougb			mask = SF_HO;
1161170222Sdougb		get_flags:
1162170222Sdougb			soptval = cb->s_flags & mask;
1163170222Sdougb			error = sooptcopyout(sopt, &soptval, sizeof soptval);
1164170222Sdougb			break;
1165170222Sdougb
1166170222Sdougb		case SO_MTU:
1167170222Sdougb			usoptval = cb->s_mtu;
1168170222Sdougb			error = sooptcopyout(sopt, &usoptval, sizeof usoptval);
1169170222Sdougb			break;
1170170222Sdougb
1171170222Sdougb		case SO_LAST_HEADER:
1172170222Sdougb			error = sooptcopyout(sopt, &cb->s_rhdr,
1173170222Sdougb					     sizeof cb->s_rhdr);
1174170222Sdougb			break;
1175170222Sdougb
1176170222Sdougb		case SO_DEFAULT_HEADERS:
1177170222Sdougb			error = sooptcopyout(sopt, &cb->s_shdr,
1178170222Sdougb					     sizeof cb->s_shdr);
1179170222Sdougb			break;
1180170222Sdougb
1181170222Sdougb		default:
1182170222Sdougb			error = ENOPROTOOPT;
1183170222Sdougb		}
1184170222Sdougb		break;
1185170222Sdougb
1186170222Sdougb	case SOPT_SET:
1187170222Sdougb		switch (sopt->sopt_name) {
1188170222Sdougb			/* XXX why are these shorts on get and ints on set?
1189170222Sdougb			   that doesn't make any sense... */
1190170222Sdougb		case SO_HEADERS_ON_INPUT:
1191170222Sdougb			mask = SF_HI;
1192170222Sdougb			goto set_head;
1193170222Sdougb
1194170222Sdougb		case SO_HEADERS_ON_OUTPUT:
1195170222Sdougb			mask = SF_HO;
1196186462Sdougb		set_head:
1197170222Sdougb			error = sooptcopyin(sopt, &optval, sizeof optval,
1198170222Sdougb					    sizeof optval);
1199170222Sdougb			if (error)
1200170222Sdougb				break;
1201170222Sdougb
1202170222Sdougb			if (cb->s_flags & SF_PI) {
1203170222Sdougb				if (optval)
1204170222Sdougb					cb->s_flags |= mask;
1205170222Sdougb				else
1206170222Sdougb					cb->s_flags &= ~mask;
1207170222Sdougb			} else error = EINVAL;
1208170222Sdougb			break;
1209170222Sdougb
1210170222Sdougb		case SO_MTU:
1211170222Sdougb			error = sooptcopyin(sopt, &usoptval, sizeof usoptval,
1212170222Sdougb					    sizeof usoptval);
1213170222Sdougb			if (error)
1214170222Sdougb				break;
1215170222Sdougb			cb->s_mtu = usoptval;
1216170222Sdougb			break;
1217170222Sdougb
1218170222Sdougb#ifdef SF_NEWCALL
1219170222Sdougb		case SO_NEWCALL:
1220170222Sdougb			error = sooptcopyin(sopt, &optval, sizeof optval,
1221170222Sdougb					    sizeof optval);
1222170222Sdougb			if (error)
1223170222Sdougb				break;
1224170222Sdougb			if (optval) {
1225170222Sdougb				cb->s_flags2 |= SF_NEWCALL;
1226170222Sdougb				spx_newchecks[5]++;
1227170222Sdougb			} else {
1228170222Sdougb				cb->s_flags2 &= ~SF_NEWCALL;
1229170222Sdougb				spx_newchecks[6]++;
1230170222Sdougb			}
1231170222Sdougb			break;
1232170222Sdougb#endif
1233170222Sdougb
1234170222Sdougb		case SO_DEFAULT_HEADERS:
1235170222Sdougb			{
1236170222Sdougb				struct spxhdr sp;
1237170222Sdougb
1238170222Sdougb				error = sooptcopyin(sopt, &sp, sizeof sp,
1239170222Sdougb						    sizeof sp);
1240170222Sdougb				if (error)
1241170222Sdougb					break;
1242170222Sdougb				cb->s_dt = sp.spx_dt;
1243170222Sdougb				cb->s_cc = sp.spx_cc & SPX_EM;
1244170222Sdougb			}
1245170222Sdougb			break;
1246170222Sdougb
1247170222Sdougb		default:
1248170222Sdougb			error = ENOPROTOOPT;
1249170222Sdougb		}
1250170222Sdougb		break;
1251170222Sdougb	}
1252170222Sdougb	return (error);
1253170222Sdougb}
1254170222Sdougb
1255170222Sdougbstatic int
1256170222Sdougbspx_usr_abort(so)
1257170222Sdougb	struct socket *so;
1258170222Sdougb{
1259170222Sdougb	int s;
1260170222Sdougb	struct ipxpcb *ipxp;
1261170222Sdougb	struct spxpcb *cb;
1262170222Sdougb
1263170222Sdougb	ipxp = sotoipxpcb(so);
1264170222Sdougb	cb = ipxtospxpcb(ipxp);
1265170222Sdougb
1266170222Sdougb	s = splnet();
1267170222Sdougb	spx_drop(cb, ECONNABORTED);
1268170222Sdougb	splx(s);
1269170222Sdougb	return (0);
1270170222Sdougb}
1271170222Sdougb
1272170222Sdougb/*
1273170222Sdougb * Accept a connection.  Essentially all the work is
1274170222Sdougb * done at higher levels; just return the address
1275170222Sdougb * of the peer, storing through addr.
1276170222Sdougb */
1277170222Sdougbstatic int
1278170222Sdougbspx_accept(so, nam)
1279170222Sdougb	struct socket *so;
1280170222Sdougb	struct sockaddr **nam;
1281170222Sdougb{
1282170222Sdougb	struct ipxpcb *ipxp;
1283170222Sdougb	struct sockaddr_ipx *sipx, ssipx;
1284170222Sdougb
1285170222Sdougb	ipxp = sotoipxpcb(so);
1286170222Sdougb	sipx = &ssipx;
1287170222Sdougb	bzero(sipx, sizeof *sipx);
1288170222Sdougb	sipx->sipx_len = sizeof *sipx;
1289170222Sdougb	sipx->sipx_family = AF_IPX;
1290170222Sdougb	sipx->sipx_addr = ipxp->ipxp_faddr;
1291170222Sdougb	*nam = sodupsockaddr((struct sockaddr *)sipx, M_NOWAIT);
1292170222Sdougb	return (0);
1293170222Sdougb}
1294170222Sdougb
1295170222Sdougbstatic int
1296170222Sdougbspx_attach(so, proto, td)
1297170222Sdougb	struct socket *so;
1298170222Sdougb	int proto;
1299170222Sdougb	struct thread *td;
1300170222Sdougb{
1301170222Sdougb	int error;
1302170222Sdougb	int s;
1303170222Sdougb	struct ipxpcb *ipxp;
1304170222Sdougb	struct spxpcb *cb;
1305170222Sdougb	struct mbuf *mm;
1306170222Sdougb	struct sockbuf *sb;
1307170222Sdougb
1308170222Sdougb	ipxp = sotoipxpcb(so);
1309170222Sdougb	cb = ipxtospxpcb(ipxp);
1310170222Sdougb
1311170222Sdougb	if (ipxp != NULL)
1312170222Sdougb		return (EISCONN);
1313170222Sdougb	s = splnet();
1314170222Sdougb	error = ipx_pcballoc(so, &ipxpcb_list, td);
1315170222Sdougb	if (error)
1316170222Sdougb		goto spx_attach_end;
1317170222Sdougb	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
1318170222Sdougb		error = soreserve(so, (u_long) 3072, (u_long) 3072);
1319170222Sdougb		if (error)
1320170222Sdougb			goto spx_attach_end;
1321170222Sdougb	}
1322170222Sdougb	ipxp = sotoipxpcb(so);
1323170222Sdougb
1324170222Sdougb	MALLOC(cb, struct spxpcb *, sizeof *cb, M_PCB, M_NOWAIT | M_ZERO);
1325170222Sdougb
1326170222Sdougb	if (cb == NULL) {
1327170222Sdougb		error = ENOBUFS;
1328170222Sdougb		goto spx_attach_end;
1329170222Sdougb	}
1330170222Sdougb	sb = &so->so_snd;
1331170222Sdougb
1332170222Sdougb	mm = m_getclr(M_DONTWAIT, MT_HEADER);
1333170222Sdougb	if (mm == NULL) {
1334170222Sdougb		FREE(cb, M_PCB);
1335170222Sdougb		error = ENOBUFS;
1336170222Sdougb		goto spx_attach_end;
1337170222Sdougb	}
1338170222Sdougb	cb->s_ipx = mtod(mm, struct ipx *);
1339170222Sdougb	cb->s_state = TCPS_LISTEN;
1340170222Sdougb	cb->s_smax = -1;
1341170222Sdougb	cb->s_swl1 = -1;
1342170222Sdougb	cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q;
1343170222Sdougb	cb->s_ipxpcb = ipxp;
1344170222Sdougb	cb->s_mtu = 576 - sizeof(struct spx);
1345170222Sdougb	cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu;
1346170222Sdougb	cb->s_ssthresh = cb->s_cwnd;
1347170222Sdougb	cb->s_cwmx = sbspace(sb) * CUNIT / (2 * sizeof(struct spx));
1348170222Sdougb	/* Above is recomputed when connecting to account
1349170222Sdougb	   for changed buffering or mtu's */
1350170222Sdougb	cb->s_rtt = SPXTV_SRTTBASE;
1351170222Sdougb	cb->s_rttvar = SPXTV_SRTTDFLT << 2;
1352170222Sdougb	SPXT_RANGESET(cb->s_rxtcur,
1353170222Sdougb	    ((SPXTV_SRTTBASE >> 2) + (SPXTV_SRTTDFLT << 2)) >> 1,
1354170222Sdougb	    SPXTV_MIN, SPXTV_REXMTMAX);
1355170222Sdougb	ipxp->ipxp_pcb = (caddr_t)cb;
1356170222Sdougbspx_attach_end:
1357170222Sdougb	splx(s);
1358170222Sdougb	return (error);
1359170222Sdougb}
1360170222Sdougb
1361170222Sdougbstatic int
1362170222Sdougbspx_bind(so, nam, td)
1363170222Sdougb	struct socket *so;
1364170222Sdougb	struct sockaddr *nam;
1365170222Sdougb	struct thread *td;
1366170222Sdougb{
1367170222Sdougb	struct ipxpcb *ipxp;
1368170222Sdougb
1369170222Sdougb	ipxp = sotoipxpcb(so);
1370170222Sdougb
1371170222Sdougb	return (ipx_pcbbind(ipxp, nam, td));
1372170222Sdougb}
1373170222Sdougb
1374170222Sdougb/*
1375170222Sdougb * Initiate connection to peer.
1376170222Sdougb * Enter SYN_SENT state, and mark socket as connecting.
1377170222Sdougb * Start keep-alive timer, setup prototype header,
1378170222Sdougb * Send initial system packet requesting connection.
1379170222Sdougb */
1380170222Sdougbstatic int
1381170222Sdougbspx_connect(so, nam, td)
1382170222Sdougb	struct socket *so;
1383170222Sdougb	struct sockaddr *nam;
1384170222Sdougb	struct thread *td;
1385186462Sdougb{
1386186462Sdougb	int error;
1387186462Sdougb	int s;
1388170222Sdougb	struct ipxpcb *ipxp;
1389170222Sdougb	struct spxpcb *cb;
1390170222Sdougb
1391186462Sdougb	ipxp = sotoipxpcb(so);
1392186462Sdougb	cb = ipxtospxpcb(ipxp);
1393170222Sdougb
1394170222Sdougb	s = splnet();
1395170222Sdougb	if (ipxp->ipxp_lport == 0) {
1396170222Sdougb		error = ipx_pcbbind(ipxp, (struct sockaddr *)NULL, td);
1397170222Sdougb		if (error)
1398170222Sdougb			goto spx_connect_end;
1399170222Sdougb	}
1400170222Sdougb	error = ipx_pcbconnect(ipxp, nam, td);
1401170222Sdougb	if (error)
1402170222Sdougb		goto spx_connect_end;
1403170222Sdougb	soisconnecting(so);
1404170222Sdougb	spxstat.spxs_connattempt++;
1405170222Sdougb	cb->s_state = TCPS_SYN_SENT;
1406170222Sdougb	cb->s_did = 0;
1407170222Sdougb	spx_template(cb);
1408170222Sdougb	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
1409170222Sdougb	cb->s_force = 1 + SPXTV_KEEP;
1410186462Sdougb	/*
1411170222Sdougb	 * Other party is required to respond to
1412170222Sdougb	 * the port I send from, but he is not
1413170222Sdougb	 * required to answer from where I am sending to,
1414170222Sdougb	 * so allow wildcarding.
1415170222Sdougb	 * original port I am sending to is still saved in
1416170222Sdougb	 * cb->s_dport.
1417170222Sdougb	 */
1418170222Sdougb	ipxp->ipxp_fport = 0;
1419170222Sdougb	error = spx_output(cb, (struct mbuf *)NULL);
1420170222Sdougbspx_connect_end:
1421170222Sdougb	splx(s);
1422170222Sdougb	return (error);
1423170222Sdougb}
1424170222Sdougb
1425170222Sdougbstatic int
1426170222Sdougbspx_detach(so)
1427170222Sdougb	struct socket *so;
1428170222Sdougb{
1429170222Sdougb	int s;
1430170222Sdougb	struct ipxpcb *ipxp;
1431170222Sdougb	struct spxpcb *cb;
1432170222Sdougb
1433170222Sdougb	ipxp = sotoipxpcb(so);
1434170222Sdougb	cb = ipxtospxpcb(ipxp);
1435170222Sdougb
1436170222Sdougb	if (ipxp == NULL)
1437170222Sdougb		return (ENOTCONN);
1438170222Sdougb	s = splnet();
1439170222Sdougb	if (cb->s_state > TCPS_LISTEN)
1440170222Sdougb		spx_disconnect(cb);
1441170222Sdougb	else
1442170222Sdougb		spx_close(cb);
1443170222Sdougb	splx(s);
1444170222Sdougb	return (0);
1445170222Sdougb}
1446170222Sdougb
1447170222Sdougb/*
1448170222Sdougb * We may decide later to implement connection closing
1449170222Sdougb * handshaking at the spx level optionally.
1450170222Sdougb * here is the hook to do it:
1451170222Sdougb */
1452170222Sdougbstatic int
1453170222Sdougbspx_usr_disconnect(so)
1454170222Sdougb	struct socket *so;
1455170222Sdougb{
1456170222Sdougb	int s;
1457170222Sdougb	struct ipxpcb *ipxp;
1458170222Sdougb	struct spxpcb *cb;
1459170222Sdougb
1460170222Sdougb	ipxp = sotoipxpcb(so);
1461170222Sdougb	cb = ipxtospxpcb(ipxp);
1462170222Sdougb
1463170222Sdougb	s = splnet();
1464170222Sdougb	spx_disconnect(cb);
1465170222Sdougb	splx(s);
1466170222Sdougb	return (0);
1467170222Sdougb}
1468170222Sdougb
1469170222Sdougbstatic int
1470170222Sdougbspx_listen(so, td)
1471170222Sdougb	struct socket *so;
1472170222Sdougb	struct thread *td;
1473170222Sdougb{
1474170222Sdougb	int error;
1475170222Sdougb	struct ipxpcb *ipxp;
1476170222Sdougb	struct spxpcb *cb;
1477170222Sdougb
1478170222Sdougb	error = 0;
1479170222Sdougb	ipxp = sotoipxpcb(so);
1480170222Sdougb	cb = ipxtospxpcb(ipxp);
1481170222Sdougb
1482170222Sdougb	if (ipxp->ipxp_lport == 0)
1483170222Sdougb		error = ipx_pcbbind(ipxp, (struct sockaddr *)NULL, td);
1484170222Sdougb	if (error == 0)
1485170222Sdougb		cb->s_state = TCPS_LISTEN;
1486170222Sdougb	return (error);
1487170222Sdougb}
1488170222Sdougb
1489170222Sdougb/*
1490170222Sdougb * After a receive, possibly send acknowledgment
1491170222Sdougb * updating allocation.
1492170222Sdougb */
1493170222Sdougbstatic int
1494170222Sdougbspx_rcvd(so, flags)
1495170222Sdougb	struct socket *so;
1496170222Sdougb	int flags;
1497170222Sdougb{
1498170222Sdougb	int s;
1499170222Sdougb	struct ipxpcb *ipxp;
1500170222Sdougb	struct spxpcb *cb;
1501170222Sdougb
1502170222Sdougb	ipxp = sotoipxpcb(so);
1503170222Sdougb	cb = ipxtospxpcb(ipxp);
1504170222Sdougb
1505170222Sdougb	s = splnet();
1506170222Sdougb	cb->s_flags |= SF_RVD;
1507170222Sdougb	spx_output(cb, (struct mbuf *)NULL);
1508170222Sdougb	cb->s_flags &= ~SF_RVD;
1509170222Sdougb	splx(s);
1510170222Sdougb	return (0);
1511170222Sdougb}
1512170222Sdougb
1513170222Sdougbstatic int
1514170222Sdougbspx_rcvoob(so, m, flags)
1515170222Sdougb	struct socket *so;
1516170222Sdougb	struct mbuf *m;
1517170222Sdougb	int flags;
1518170222Sdougb{
1519170222Sdougb	struct ipxpcb *ipxp;
1520170222Sdougb	struct spxpcb *cb;
1521170222Sdougb
1522170222Sdougb	ipxp = sotoipxpcb(so);
1523170222Sdougb	cb = ipxtospxpcb(ipxp);
1524170222Sdougb
1525170222Sdougb	if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark ||
1526170222Sdougb	    (so->so_rcv.sb_state & SBS_RCVATMARK)) {
1527170222Sdougb		m->m_len = 1;
1528170222Sdougb		*mtod(m, caddr_t) = cb->s_iobc;
1529170222Sdougb		return (0);
1530170222Sdougb	}
1531170222Sdougb	return (EINVAL);
1532170222Sdougb}
1533170222Sdougb
1534170222Sdougbstatic int
1535170222Sdougbspx_send(so, flags, m, addr, controlp, td)
1536170222Sdougb	struct socket *so;
1537170222Sdougb	int flags;
1538170222Sdougb	struct mbuf *m;
1539170222Sdougb	struct sockaddr *addr;
1540170222Sdougb	struct mbuf *controlp;
1541170222Sdougb	struct thread *td;
1542170222Sdougb{
1543170222Sdougb	int error;
1544170222Sdougb	int s;
1545170222Sdougb	struct ipxpcb *ipxp;
1546170222Sdougb	struct spxpcb *cb;
1547170222Sdougb
1548170222Sdougb	error = 0;
1549170222Sdougb	ipxp = sotoipxpcb(so);
1550170222Sdougb	cb = ipxtospxpcb(ipxp);
1551170222Sdougb
1552170222Sdougb	s = splnet();
1553170222Sdougb	if (flags & PRUS_OOB) {
1554170222Sdougb		if (sbspace(&so->so_snd) < -512) {
1555170222Sdougb			error = ENOBUFS;
1556170222Sdougb			goto spx_send_end;
1557170222Sdougb		}
1558170222Sdougb		cb->s_oobflags |= SF_SOOB;
1559170222Sdougb	}
1560170222Sdougb	if (controlp != NULL) {
1561170222Sdougb		u_short *p = mtod(controlp, u_short *);
1562170222Sdougb		spx_newchecks[2]++;
1563170222Sdougb		if ((p[0] == 5) && (p[1] == 1)) { /* XXXX, for testing */
1564170222Sdougb			cb->s_shdr.spx_dt = *(u_char *)(&p[2]);
1565170222Sdougb			spx_newchecks[3]++;
1566170222Sdougb		}
1567170222Sdougb		m_freem(controlp);
1568170222Sdougb	}
1569170222Sdougb	controlp = NULL;
1570170222Sdougb	error = spx_output(cb, m);
1571170222Sdougb	m = NULL;
1572170222Sdougbspx_send_end:
1573170222Sdougb	if (controlp != NULL)
1574170222Sdougb		m_freem(controlp);
1575170222Sdougb	if (m != NULL)
1576170222Sdougb		m_freem(m);
1577170222Sdougb	splx(s);
1578170222Sdougb	return (error);
1579170222Sdougb}
1580170222Sdougb
1581170222Sdougbstatic int
1582170222Sdougbspx_shutdown(so)
1583170222Sdougb	struct socket *so;
1584170222Sdougb{
1585170222Sdougb	int error;
1586170222Sdougb	int s;
1587170222Sdougb	struct ipxpcb *ipxp;
1588170222Sdougb	struct spxpcb *cb;
1589170222Sdougb
1590170222Sdougb	error = 0;
1591170222Sdougb	ipxp = sotoipxpcb(so);
1592170222Sdougb	cb = ipxtospxpcb(ipxp);
1593170222Sdougb
1594170222Sdougb	s = splnet();
1595170222Sdougb	socantsendmore(so);
1596170222Sdougb	cb = spx_usrclosed(cb);
1597170222Sdougb	if (cb != NULL)
1598170222Sdougb		error = spx_output(cb, (struct mbuf *)NULL);
1599170222Sdougb	splx(s);
1600170222Sdougb	return (error);
1601170222Sdougb}
1602170222Sdougb
1603170222Sdougbstatic int
1604170222Sdougbspx_sp_attach(so, proto, td)
1605170222Sdougb	struct socket *so;
1606170222Sdougb	int proto;
1607170222Sdougb	struct thread *td;
1608170222Sdougb{
1609170222Sdougb	int error;
1610170222Sdougb	struct ipxpcb *ipxp;
1611170222Sdougb
1612170222Sdougb	error = spx_attach(so, proto, td);
1613170222Sdougb	if (error == 0) {
1614170222Sdougb		ipxp = sotoipxpcb(so);
1615170222Sdougb		((struct spxpcb *)ipxp->ipxp_pcb)->s_flags |=
1616170222Sdougb					(SF_HI | SF_HO | SF_PI);
1617170222Sdougb	}
1618170222Sdougb	return (error);
1619170222Sdougb}
1620170222Sdougb
1621170222Sdougb/*
1622170222Sdougb * Create template to be used to send spx packets on a connection.
1623170222Sdougb * Called after host entry created, fills
1624170222Sdougb * in a skeletal spx header (choosing connection id),
1625170222Sdougb * minimizing the amount of work necessary when the connection is used.
1626170222Sdougb */
1627170222Sdougbstatic void
1628170222Sdougbspx_template(cb)
1629170222Sdougb	register struct spxpcb *cb;
1630170222Sdougb{
1631170222Sdougb	register struct ipxpcb *ipxp = cb->s_ipxpcb;
1632170222Sdougb	register struct ipx *ipx = cb->s_ipx;
1633170222Sdougb	register struct sockbuf *sb = &(ipxp->ipxp_socket->so_snd);
1634170222Sdougb
1635170222Sdougb	ipx->ipx_pt = IPXPROTO_SPX;
1636170222Sdougb	ipx->ipx_sna = ipxp->ipxp_laddr;
1637170222Sdougb	ipx->ipx_dna = ipxp->ipxp_faddr;
1638170222Sdougb	cb->s_sid = htons(spx_iss);
1639170222Sdougb	spx_iss += SPX_ISSINCR/2;
1640170222Sdougb	cb->s_alo = 1;
1641170222Sdougb	cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu;
1642170222Sdougb	cb->s_ssthresh = cb->s_cwnd; /* Try to expand fast to full complement
1643170222Sdougb					of large packets */
1644170222Sdougb	cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spx));
1645170222Sdougb	cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd);
1646170222Sdougb		/* But allow for lots of little packets as well */
1647170222Sdougb}
1648170222Sdougb
1649170222Sdougb/*
1650170222Sdougb * Close a SPIP control block:
1651170222Sdougb *	discard spx control block itself
1652170222Sdougb *	discard ipx protocol control block
1653170222Sdougb *	wake up any sleepers
1654170222Sdougb */
1655170222Sdougbstatic struct spxpcb *
1656170222Sdougbspx_close(cb)
1657170222Sdougb	register struct spxpcb *cb;
1658170222Sdougb{
1659170222Sdougb	register struct spx_q *s;
1660170222Sdougb	struct ipxpcb *ipxp = cb->s_ipxpcb;
1661170222Sdougb	struct socket *so = ipxp->ipxp_socket;
1662170222Sdougb	register struct mbuf *m;
1663170222Sdougb
1664254402Serwin	s = cb->s_q.si_next;
1665170222Sdougb	while (s != &(cb->s_q)) {
1666254402Serwin		s = s->si_next;
1667254402Serwin		m = dtom(s->si_prev);
1668170222Sdougb		remque(s->si_prev);
1669170222Sdougb		m_freem(m);
1670170222Sdougb	}
1671254402Serwin	m_free(dtom(cb->s_ipx));
1672254402Serwin	FREE(cb, M_PCB);
1673254402Serwin	ipxp->ipxp_pcb = 0;
1674254402Serwin	soisdisconnected(so);
1675170222Sdougb	ipx_pcbdetach(ipxp);
1676170222Sdougb	spxstat.spxs_closed++;
1677170222Sdougb	return ((struct spxpcb *)NULL);
1678262706Serwin}
1679262706Serwin
1680170222Sdougb/*
1681170222Sdougb *	Someday we may do level 3 handshaking
1682170222Sdougb *	to close a connection or send a xerox style error.
1683170222Sdougb *	For now, just close.
1684170222Sdougb */
1685170222Sdougbstatic struct spxpcb *
1686170222Sdougbspx_usrclosed(cb)
1687170222Sdougb	register struct spxpcb *cb;
1688170222Sdougb{
1689170222Sdougb	return (spx_close(cb));
1690170222Sdougb}
1691170222Sdougb
1692170222Sdougbstatic struct spxpcb *
1693170222Sdougbspx_disconnect(cb)
1694170222Sdougb	register struct spxpcb *cb;
1695254402Serwin{
1696254402Serwin	return (spx_close(cb));
1697170222Sdougb}
1698170222Sdougb
1699170222Sdougb/*
1700170222Sdougb * Drop connection, reporting
1701170222Sdougb * the specified error.
1702170222Sdougb */
1703170222Sdougbstatic struct spxpcb *
1704170222Sdougbspx_drop(cb, errno)
1705170222Sdougb	register struct spxpcb *cb;
1706170222Sdougb	int errno;
1707170222Sdougb{
1708170222Sdougb	struct socket *so = cb->s_ipxpcb->ipxp_socket;
1709170222Sdougb
1710170222Sdougb	/*
1711170222Sdougb	 * someday, in the xerox world
1712170222Sdougb	 * we will generate error protocol packets
1713170222Sdougb	 * announcing that the socket has gone away.
1714170222Sdougb	 */
1715170222Sdougb	if (TCPS_HAVERCVDSYN(cb->s_state)) {
1716170222Sdougb		spxstat.spxs_drops++;
1717170222Sdougb		cb->s_state = TCPS_CLOSED;
1718170222Sdougb		/*tcp_output(cb);*/
1719170222Sdougb	} else
1720170222Sdougb		spxstat.spxs_conndrops++;
1721170222Sdougb	so->so_error = errno;
1722170222Sdougb	return (spx_close(cb));
1723170222Sdougb}
1724170222Sdougb
1725170222Sdougb/*
1726170222Sdougb * Fast timeout routine for processing delayed acks
1727170222Sdougb */
1728170222Sdougbvoid
1729170222Sdougbspx_fasttimo()
1730170222Sdougb{
1731170222Sdougb	register struct ipxpcb *ipxp;
1732170222Sdougb	register struct spxpcb *cb;
1733170222Sdougb	int s = splnet();
1734170222Sdougb
1735170222Sdougb	LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) {
1736170222Sdougb		if ((cb = (struct spxpcb *)ipxp->ipxp_pcb) != NULL &&
1737170222Sdougb		    (cb->s_flags & SF_DELACK)) {
1738170222Sdougb			cb->s_flags &= ~SF_DELACK;
1739170222Sdougb			cb->s_flags |= SF_ACKNOW;
1740170222Sdougb			spxstat.spxs_delack++;
1741170222Sdougb			spx_output(cb, (struct mbuf *)NULL);
1742170222Sdougb		}
1743170222Sdougb	}
1744170222Sdougb
1745170222Sdougb	splx(s);
1746170222Sdougb}
1747170222Sdougb
1748170222Sdougb/*
1749170222Sdougb * spx protocol timeout routine called every 500 ms.
1750170222Sdougb * Updates the timers in all active pcb's and
1751170222Sdougb * causes finite state machine actions if timers expire.
1752170222Sdougb */
1753170222Sdougbvoid
1754170222Sdougbspx_slowtimo()
1755170222Sdougb{
1756170222Sdougb	register struct ipxpcb *ip, *ip_temp;
1757170222Sdougb	register struct spxpcb *cb;
1758170222Sdougb	int s = splnet();
1759170222Sdougb	register int i;
1760170222Sdougb
1761170222Sdougb	/*
1762170222Sdougb	 * Search through tcb's and update active timers.  Note that timers
1763170222Sdougb	 * may free the ipxpcb, so be sure to handle that case.
1764170222Sdougb	 */
1765186462Sdougb	LIST_FOREACH_SAFE(ip, &ipxpcb_list, ipxp_list, ip_temp) {
1766170222Sdougb		cb = ipxtospxpcb(ip);
1767170222Sdougb		if (cb == NULL)
1768170222Sdougb			continue;
1769170222Sdougb		for (i = 0; i < SPXT_NTIMERS; i++) {
1770170222Sdougb			if (cb->s_timer[i] && --cb->s_timer[i] == 0) {
1771170222Sdougb				/*
1772170222Sdougb				 * spx_timers() returns (NULL) if it free'd
1773170222Sdougb				 * the pcb.
1774170222Sdougb				 */
1775170222Sdougb				if (spx_timers(cb, i) == NULL)
1776170222Sdougb					continue;
1777170222Sdougb			}
1778170222Sdougb		}
1779170222Sdougb		cb->s_idle++;
1780170222Sdougb		if (cb->s_rtt)
1781170222Sdougb			cb->s_rtt++;
1782170222Sdougb	}
1783170222Sdougb	spx_iss += SPX_ISSINCR/PR_SLOWHZ;		/* increment iss */
1784170222Sdougb	splx(s);
1785254897Serwin}
1786254897Serwin
1787170222Sdougb/*
1788170222Sdougb * SPX timer processing.
1789170222Sdougb */
1790254402Serwinstatic struct spxpcb *
1791170222Sdougbspx_timers(cb, timer)
1792170222Sdougb	register struct spxpcb *cb;
1793170222Sdougb	int timer;
1794170222Sdougb{
1795170222Sdougb	long rexmt;
1796254402Serwin	int win;
1797170222Sdougb
1798170222Sdougb	cb->s_force = 1 + timer;
1799170222Sdougb	switch (timer) {
1800170222Sdougb
1801170222Sdougb	/*
1802	 * 2 MSL timeout in shutdown went off.  TCP deletes connection
1803	 * control block.
1804	 */
1805	case SPXT_2MSL:
1806		printf("spx: SPXT_2MSL went off for no reason\n");
1807		cb->s_timer[timer] = 0;
1808		break;
1809
1810	/*
1811	 * Retransmission timer went off.  Message has not
1812	 * been acked within retransmit interval.  Back off
1813	 * to a longer retransmit interval and retransmit one packet.
1814	 */
1815	case SPXT_REXMT:
1816		if (++cb->s_rxtshift > SPX_MAXRXTSHIFT) {
1817			cb->s_rxtshift = SPX_MAXRXTSHIFT;
1818			spxstat.spxs_timeoutdrop++;
1819			cb = spx_drop(cb, ETIMEDOUT);
1820			break;
1821		}
1822		spxstat.spxs_rexmttimeo++;
1823		rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
1824		rexmt *= spx_backoff[cb->s_rxtshift];
1825		SPXT_RANGESET(cb->s_rxtcur, rexmt, SPXTV_MIN, SPXTV_REXMTMAX);
1826		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1827		/*
1828		 * If we have backed off fairly far, our srtt
1829		 * estimate is probably bogus.  Clobber it
1830		 * so we'll take the next rtt measurement as our srtt;
1831		 * move the current srtt into rttvar to keep the current
1832		 * retransmit times until then.
1833		 */
1834		if (cb->s_rxtshift > SPX_MAXRXTSHIFT / 4 ) {
1835			cb->s_rttvar += (cb->s_srtt >> 2);
1836			cb->s_srtt = 0;
1837		}
1838		cb->s_snxt = cb->s_rack;
1839		/*
1840		 * If timing a packet, stop the timer.
1841		 */
1842		cb->s_rtt = 0;
1843		/*
1844		 * See very long discussion in tcp_timer.c about congestion
1845		 * window and sstrhesh
1846		 */
1847		win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2;
1848		if (win < 2)
1849			win = 2;
1850		cb->s_cwnd = CUNIT;
1851		cb->s_ssthresh = win * CUNIT;
1852		spx_output(cb, (struct mbuf *)NULL);
1853		break;
1854
1855	/*
1856	 * Persistance timer into zero window.
1857	 * Force a probe to be sent.
1858	 */
1859	case SPXT_PERSIST:
1860		spxstat.spxs_persisttimeo++;
1861		spx_setpersist(cb);
1862		spx_output(cb, (struct mbuf *)NULL);
1863		break;
1864
1865	/*
1866	 * Keep-alive timer went off; send something
1867	 * or drop connection if idle for too long.
1868	 */
1869	case SPXT_KEEP:
1870		spxstat.spxs_keeptimeo++;
1871		if (cb->s_state < TCPS_ESTABLISHED)
1872			goto dropit;
1873		if (cb->s_ipxpcb->ipxp_socket->so_options & SO_KEEPALIVE) {
1874		    	if (cb->s_idle >= SPXTV_MAXIDLE)
1875				goto dropit;
1876			spxstat.spxs_keepprobe++;
1877			spx_output(cb, (struct mbuf *)NULL);
1878		} else
1879			cb->s_idle = 0;
1880		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
1881		break;
1882	dropit:
1883		spxstat.spxs_keepdrops++;
1884		cb = spx_drop(cb, ETIMEDOUT);
1885		break;
1886	}
1887	return (cb);
1888}
1889