spx_reass.c revision 160549
1/*-
2 * Copyright (c) 1984, 1985, 1986, 1987, 1993
3 *	The Regents of the University of California.
4 * Copyright (c) 1995, Mike Mitchell
5 * Copyright (c) 2004-2006 Robert N. M. Watson
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	@(#)spx_usrreq.h
37 */
38
39#include <sys/cdefs.h>
40__FBSDID("$FreeBSD: head/sys/netipx/spx_usrreq.c 160549 2006-07-21 17:11:15Z rwatson $");
41
42#include <sys/param.h>
43#include <sys/lock.h>
44#include <sys/malloc.h>
45#include <sys/mbuf.h>
46#include <sys/mutex.h>
47#include <sys/proc.h>
48#include <sys/protosw.h>
49#include <sys/signalvar.h>
50#include <sys/socket.h>
51#include <sys/socketvar.h>
52#include <sys/sx.h>
53#include <sys/systm.h>
54
55#include <net/route.h>
56#include <netinet/tcp_fsm.h>
57
58#include <netipx/ipx.h>
59#include <netipx/ipx_pcb.h>
60#include <netipx/ipx_var.h>
61#include <netipx/spx.h>
62#include <netipx/spx_debug.h>
63#include <netipx/spx_timer.h>
64#include <netipx/spx_var.h>
65
66/*
67 * SPX protocol implementation.
68 */
69static struct	mtx spx_mtx;			/* Protects only spx_iss. */
70static u_short 	spx_iss;
71static u_short	spx_newchecks[50];
72static int	spx_hardnosed;
73static int	spx_use_delack = 0;
74static int	traceallspxs = 0;
75static struct	spx_istat spx_istat;
76static int	spxrexmtthresh = 3;
77
78#define	SPX_LOCK_INIT()	mtx_init(&spx_mtx, "spx_mtx", NULL, MTX_DEF)
79#define	SPX_LOCK()	mtx_lock(&spx_mtx)
80#define	SPX_UNLOCK()	mtx_unlock(&spx_mtx)
81
82/* Following was struct spxstat spxstat; */
83#ifndef spxstat
84#define spxstat spx_istat.newstats
85#endif
86
87static const int spx_backoff[SPX_MAXRXTSHIFT+1] =
88    { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
89
90static	void spx_close(struct spxpcb *cb);
91static	void spx_disconnect(struct spxpcb *cb);
92static	void spx_drop(struct spxpcb *cb, int errno);
93static	int spx_output(struct spxpcb *cb, struct mbuf *m0);
94static	int spx_reass(struct spxpcb *cb, struct spx *si);
95static	void spx_setpersist(struct spxpcb *cb);
96static	void spx_template(struct spxpcb *cb);
97static	void spx_timers(struct spxpcb *cb, int timer);
98static	void spx_usrclosed(struct spxpcb *cb);
99
100static	void spx_usr_abort(struct socket *so);
101static	int spx_accept(struct socket *so, struct sockaddr **nam);
102static	int spx_attach(struct socket *so, int proto, struct thread *td);
103static	int spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td);
104static	void spx_usr_close(struct socket *so);
105static	int spx_connect(struct socket *so, struct sockaddr *nam,
106			struct thread *td);
107static	void spx_detach(struct socket *so);
108static	void spx_pcbdetach(struct ipxpcb *ipxp);
109static	int spx_usr_disconnect(struct socket *so);
110static	int spx_listen(struct socket *so, int backlog, struct thread *td);
111static	int spx_rcvd(struct socket *so, int flags);
112static	int spx_rcvoob(struct socket *so, struct mbuf *m, int flags);
113static	int spx_send(struct socket *so, int flags, struct mbuf *m,
114		     struct sockaddr *addr, struct mbuf *control,
115		     struct thread *td);
116static	int spx_shutdown(struct socket *so);
117static	int spx_sp_attach(struct socket *so, int proto, struct thread *td);
118
119struct	pr_usrreqs spx_usrreqs = {
120	.pru_abort =		spx_usr_abort,
121	.pru_accept =		spx_accept,
122	.pru_attach =		spx_attach,
123	.pru_bind =		spx_bind,
124	.pru_connect =		spx_connect,
125	.pru_control =		ipx_control,
126	.pru_detach =		spx_detach,
127	.pru_disconnect =	spx_usr_disconnect,
128	.pru_listen =		spx_listen,
129	.pru_peeraddr =		ipx_peeraddr,
130	.pru_rcvd =		spx_rcvd,
131	.pru_rcvoob =		spx_rcvoob,
132	.pru_send =		spx_send,
133	.pru_shutdown =		spx_shutdown,
134	.pru_sockaddr =		ipx_sockaddr,
135	.pru_close =		spx_usr_close,
136};
137
138struct	pr_usrreqs spx_usrreq_sps = {
139	.pru_abort =		spx_usr_abort,
140	.pru_accept =		spx_accept,
141	.pru_attach =		spx_sp_attach,
142	.pru_bind =		spx_bind,
143	.pru_connect =		spx_connect,
144	.pru_control =		ipx_control,
145	.pru_detach =		spx_detach,
146	.pru_disconnect =	spx_usr_disconnect,
147	.pru_listen =		spx_listen,
148	.pru_peeraddr =		ipx_peeraddr,
149	.pru_rcvd =		spx_rcvd,
150	.pru_rcvoob =		spx_rcvoob,
151	.pru_send =		spx_send,
152	.pru_shutdown =		spx_shutdown,
153	.pru_sockaddr =		ipx_sockaddr,
154	.pru_close =		spx_usr_close,
155};
156
157void
158spx_init(void)
159{
160
161	SPX_LOCK_INIT();
162	spx_iss = 1; /* WRONG !! should fish it out of TODR */
163}
164
165void
166spx_input(struct mbuf *m, struct ipxpcb *ipxp)
167{
168	struct spxpcb *cb;
169	struct spx *si = mtod(m, struct spx *);
170	struct socket *so;
171	struct spx spx_savesi;
172	int dropsocket = 0;
173	short ostate = 0;
174
175	spxstat.spxs_rcvtotal++;
176	KASSERT(ipxp != NULL, ("spx_input: ipxpcb == NULL"));
177
178	/*
179	 * spx_input() assumes that the caller will hold both the pcb list
180	 * lock and also the ipxp lock.  spx_input() will release both before
181	 * returning, and may in fact trade in the ipxp lock for another pcb
182	 * lock following sonewconn().
183	 */
184	IPX_LIST_LOCK_ASSERT();
185	IPX_LOCK_ASSERT(ipxp);
186
187	cb = ipxtospxpcb(ipxp);
188	KASSERT(cb != NULL, ("spx_input: cb == NULL"));
189
190	if (ipxp->ipxp_flags & IPXP_DROPPED)
191		goto drop;
192
193	if (m->m_len < sizeof(*si)) {
194		if ((m = m_pullup(m, sizeof(*si))) == NULL) {
195			IPX_UNLOCK(ipxp);
196			IPX_LIST_UNLOCK();
197			spxstat.spxs_rcvshort++;
198			return;
199		}
200		si = mtod(m, struct spx *);
201	}
202	si->si_seq = ntohs(si->si_seq);
203	si->si_ack = ntohs(si->si_ack);
204	si->si_alo = ntohs(si->si_alo);
205
206	so = ipxp->ipxp_socket;
207	KASSERT(so != NULL, ("spx_input: so == NULL"));
208
209	if (so->so_options & SO_DEBUG || traceallspxs) {
210		ostate = cb->s_state;
211		spx_savesi = *si;
212	}
213	if (so->so_options & SO_ACCEPTCONN) {
214		struct spxpcb *ocb = cb;
215
216		so = sonewconn(so, 0);
217		if (so == NULL)
218			goto drop;
219
220		/*
221		 * This is ugly, but ....
222		 *
223		 * Mark socket as temporary until we're committed to keeping
224		 * it.  The code at ``drop'' and ``dropwithreset'' check the
225		 * flag dropsocket to see if the temporary socket created
226		 * here should be discarded.  We mark the socket as
227		 * discardable until we're committed to it below in
228		 * TCPS_LISTEN.
229		 *
230		 * XXXRW: In the new world order of real kernel parallelism,
231		 * temporarily allocating the socket when we're "not sure"
232		 * seems like a bad idea, as we might race to remove it if
233		 * the listen socket is closed...?
234		 *
235		 * We drop the lock of the listen socket ipxp, and acquire
236		 * the lock of the new socket ippx.
237		 */
238		dropsocket++;
239		IPX_UNLOCK(ipxp);
240		ipxp = (struct ipxpcb *)so->so_pcb;
241		IPX_LOCK(ipxp);
242		ipxp->ipxp_laddr = si->si_dna;
243		cb = ipxtospxpcb(ipxp);
244		cb->s_mtu = ocb->s_mtu;		/* preserve sockopts */
245		cb->s_flags = ocb->s_flags;	/* preserve sockopts */
246		cb->s_flags2 = ocb->s_flags2;	/* preserve sockopts */
247		cb->s_state = TCPS_LISTEN;
248	}
249	IPX_LOCK_ASSERT(ipxp);
250
251	/*
252	 * Packet received on connection.  Reset idle time and keep-alive
253	 * timer.
254	 */
255	cb->s_idle = 0;
256	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
257
258	switch (cb->s_state) {
259	case TCPS_LISTEN:{
260		struct sockaddr_ipx *sipx, ssipx;
261		struct ipx_addr laddr;
262
263		/*
264		 * If somebody here was carying on a conversation and went
265		 * away, and his pen pal thinks he can still talk, we get the
266		 * misdirected packet.
267		 */
268		if (spx_hardnosed && (si->si_did != 0 || si->si_seq != 0)) {
269			spx_istat.gonawy++;
270			goto dropwithreset;
271		}
272		sipx = &ssipx;
273		bzero(sipx, sizeof *sipx);
274		sipx->sipx_len = sizeof(*sipx);
275		sipx->sipx_family = AF_IPX;
276		sipx->sipx_addr = si->si_sna;
277		laddr = ipxp->ipxp_laddr;
278		if (ipx_nullhost(laddr))
279			ipxp->ipxp_laddr = si->si_dna;
280		if (ipx_pcbconnect(ipxp, (struct sockaddr *)sipx, &thread0)) {
281			ipxp->ipxp_laddr = laddr;
282			spx_istat.noconn++;
283			goto drop;
284		}
285		spx_template(cb);
286		dropsocket = 0;		/* committed to socket */
287		cb->s_did = si->si_sid;
288		cb->s_rack = si->si_ack;
289		cb->s_ralo = si->si_alo;
290#define THREEWAYSHAKE
291#ifdef THREEWAYSHAKE
292		cb->s_state = TCPS_SYN_RECEIVED;
293		cb->s_force = 1 + SPXT_KEEP;
294		spxstat.spxs_accepts++;
295		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
296		}
297		break;
298
299	 case TCPS_SYN_RECEIVED: {
300		/*
301		 * This state means that we have heard a response to our
302		 * acceptance of their connection.  It is probably logically
303		 * unnecessary in this implementation.
304		 */
305		if (si->si_did != cb->s_sid) {
306			spx_istat.wrncon++;
307			goto drop;
308		}
309#endif
310		ipxp->ipxp_fport =  si->si_sport;
311		cb->s_timer[SPXT_REXMT] = 0;
312		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
313		soisconnected(so);
314		cb->s_state = TCPS_ESTABLISHED;
315		spxstat.spxs_accepts++;
316		}
317		break;
318
319	case TCPS_SYN_SENT:
320		/*
321		 * This state means that we have gotten a response to our
322		 * attempt to establish a connection.  We fill in the data
323		 * from the other side, telling us which port to respond to,
324		 * instead of the well-known one we might have sent to in the
325		 * first place.  We also require that this is a response to
326		 * our connection id.
327		 */
328		if (si->si_did != cb->s_sid) {
329			spx_istat.notme++;
330			goto drop;
331		}
332		spxstat.spxs_connects++;
333		cb->s_did = si->si_sid;
334		cb->s_rack = si->si_ack;
335		cb->s_ralo = si->si_alo;
336		cb->s_dport = ipxp->ipxp_fport =  si->si_sport;
337		cb->s_timer[SPXT_REXMT] = 0;
338		cb->s_flags |= SF_ACKNOW;
339		soisconnected(so);
340		cb->s_state = TCPS_ESTABLISHED;
341		/*
342		 * Use roundtrip time of connection request for initial rtt.
343		 */
344		if (cb->s_rtt) {
345			cb->s_srtt = cb->s_rtt << 3;
346			cb->s_rttvar = cb->s_rtt << 1;
347			SPXT_RANGESET(cb->s_rxtcur,
348			    ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
349			    SPXTV_MIN, SPXTV_REXMTMAX);
350			    cb->s_rtt = 0;
351		}
352	}
353
354	if (so->so_options & SO_DEBUG || traceallspxs)
355		spx_trace(SA_INPUT, (u_char)ostate, cb, &spx_savesi, 0);
356
357	m->m_len -= sizeof(struct ipx);
358	m->m_pkthdr.len -= sizeof(struct ipx);
359	m->m_data += sizeof(struct ipx);
360
361	if (spx_reass(cb, si))
362		m_freem(m);
363	if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT)))
364		spx_output(cb, NULL);
365	cb->s_flags &= ~(SF_WIN|SF_RXT);
366	IPX_UNLOCK(ipxp);
367	IPX_LIST_UNLOCK();
368	return;
369
370dropwithreset:
371	IPX_LOCK_ASSERT(ipxp);
372	if (cb == NULL || (cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG ||
373	    traceallspxs))
374		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
375	IPX_UNLOCK(ipxp);
376	if (dropsocket) {
377		struct socket *head;
378		ACCEPT_LOCK();
379		KASSERT((so->so_qstate & SQ_INCOMP) != 0,
380		    ("spx_input: nascent socket not SQ_INCOMP on soabort()"));
381		head = so->so_head;
382		TAILQ_REMOVE(&head->so_incomp, so, so_list);
383		head->so_incqlen--;
384		so->so_qstate &= ~SQ_INCOMP;
385		so->so_head = NULL;
386		ACCEPT_UNLOCK();
387		soabort(so);
388	}
389	IPX_LIST_UNLOCK();
390	m_freem(dtom(si));
391	return;
392
393drop:
394	IPX_LOCK_ASSERT(ipxp);
395	if (cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || traceallspxs)
396		spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0);
397	IPX_UNLOCK(ipxp);
398	IPX_LIST_UNLOCK();
399	m_freem(m);
400}
401
402/*
403 * This is structurally similar to the tcp reassembly routine but its
404 * function is somewhat different:  It merely queues packets up, and
405 * suppresses duplicates.
406 */
407static int
408spx_reass(struct spxpcb *cb, struct spx *si)
409{
410	struct spx_q *q;
411	struct mbuf *m;
412	struct socket *so = cb->s_ipxpcb->ipxp_socket;
413	char packetp = cb->s_flags & SF_HI;
414	int incr;
415	char wakeup = 0;
416
417	IPX_LOCK_ASSERT(cb->s_ipxpcb);
418
419	if (si == SI(0))
420		goto present;
421	/*
422	 * Update our news from them.
423	 */
424	if (si->si_cc & SPX_SA)
425		cb->s_flags |= (spx_use_delack ? SF_DELACK : SF_ACKNOW);
426	if (SSEQ_GT(si->si_alo, cb->s_ralo))
427		cb->s_flags |= SF_WIN;
428	if (SSEQ_LEQ(si->si_ack, cb->s_rack)) {
429		if ((si->si_cc & SPX_SP) && cb->s_rack != (cb->s_smax + 1)) {
430			spxstat.spxs_rcvdupack++;
431			/*
432			 * If this is a completely duplicate ack and other
433			 * conditions hold, we assume a packet has been
434			 * dropped and retransmit it exactly as in
435			 * tcp_input().
436			 */
437			if (si->si_ack != cb->s_rack ||
438			    si->si_alo != cb->s_ralo)
439				cb->s_dupacks = 0;
440			else if (++cb->s_dupacks == spxrexmtthresh) {
441				u_short onxt = cb->s_snxt;
442				int cwnd = cb->s_cwnd;
443
444				cb->s_snxt = si->si_ack;
445				cb->s_cwnd = CUNIT;
446				cb->s_force = 1 + SPXT_REXMT;
447				spx_output(cb, NULL);
448				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
449				cb->s_rtt = 0;
450				if (cwnd >= 4 * CUNIT)
451					cb->s_cwnd = cwnd / 2;
452				if (SSEQ_GT(onxt, cb->s_snxt))
453					cb->s_snxt = onxt;
454				return (1);
455			}
456		} else
457			cb->s_dupacks = 0;
458		goto update_window;
459	}
460	cb->s_dupacks = 0;
461
462	/*
463	 * If our correspondent acknowledges data we haven't sent TCP would
464	 * drop the packet after acking.  We'll be a little more permissive.
465	 */
466	if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) {
467		spxstat.spxs_rcvacktoomuch++;
468		si->si_ack = cb->s_smax + 1;
469	}
470	spxstat.spxs_rcvackpack++;
471
472	/*
473	 * If transmit timer is running and timed sequence number was acked,
474	 * update smoothed round trip time.  See discussion of algorithm in
475	 * tcp_input.c
476	 */
477	if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) {
478		spxstat.spxs_rttupdated++;
479		if (cb->s_srtt != 0) {
480			short delta;
481			delta = cb->s_rtt - (cb->s_srtt >> 3);
482			if ((cb->s_srtt += delta) <= 0)
483				cb->s_srtt = 1;
484			if (delta < 0)
485				delta = -delta;
486			delta -= (cb->s_rttvar >> 2);
487			if ((cb->s_rttvar += delta) <= 0)
488				cb->s_rttvar = 1;
489		} else {
490			/*
491			 * No rtt measurement yet.
492			 */
493			cb->s_srtt = cb->s_rtt << 3;
494			cb->s_rttvar = cb->s_rtt << 1;
495		}
496		cb->s_rtt = 0;
497		cb->s_rxtshift = 0;
498		SPXT_RANGESET(cb->s_rxtcur,
499			((cb->s_srtt >> 2) + cb->s_rttvar) >> 1,
500			SPXTV_MIN, SPXTV_REXMTMAX);
501	}
502
503	/*
504	 * If all outstanding data is acked, stop retransmit timer and
505	 * remember to restart (more output or persist).  If there is more
506	 * data to be acked, restart retransmit timer, using current
507	 * (possibly backed-off) value;
508	 */
509	if (si->si_ack == cb->s_smax + 1) {
510		cb->s_timer[SPXT_REXMT] = 0;
511		cb->s_flags |= SF_RXT;
512	} else if (cb->s_timer[SPXT_PERSIST] == 0)
513		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
514
515	/*
516	 * When new data is acked, open the congestion window.  If the window
517	 * gives us less than ssthresh packets in flight, open exponentially
518	 * (maxseg at a time).  Otherwise open linearly (maxseg^2 / cwnd at a
519	 * time).
520	 */
521	incr = CUNIT;
522	if (cb->s_cwnd > cb->s_ssthresh)
523		incr = max(incr * incr / cb->s_cwnd, 1);
524	cb->s_cwnd = min(cb->s_cwnd + incr, cb->s_cwmx);
525
526	/*
527	 * Trim Acked data from output queue.
528	 */
529	SOCKBUF_LOCK(&so->so_snd);
530	while ((m = so->so_snd.sb_mb) != NULL) {
531		if (SSEQ_LT((mtod(m, struct spx *))->si_seq, si->si_ack))
532			sbdroprecord_locked(&so->so_snd);
533		else
534			break;
535	}
536	sowwakeup_locked(so);
537	cb->s_rack = si->si_ack;
538update_window:
539	if (SSEQ_LT(cb->s_snxt, cb->s_rack))
540		cb->s_snxt = cb->s_rack;
541	if (SSEQ_LT(cb->s_swl1, si->si_seq) || ((cb->s_swl1 == si->si_seq &&
542	    (SSEQ_LT(cb->s_swl2, si->si_ack))) ||
543	     (cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo)))) {
544		/* keep track of pure window updates */
545		if ((si->si_cc & SPX_SP) && cb->s_swl2 == si->si_ack
546		    && SSEQ_LT(cb->s_ralo, si->si_alo)) {
547			spxstat.spxs_rcvwinupd++;
548			spxstat.spxs_rcvdupack--;
549		}
550		cb->s_ralo = si->si_alo;
551		cb->s_swl1 = si->si_seq;
552		cb->s_swl2 = si->si_ack;
553		cb->s_swnd = (1 + si->si_alo - si->si_ack);
554		if (cb->s_swnd > cb->s_smxw)
555			cb->s_smxw = cb->s_swnd;
556		cb->s_flags |= SF_WIN;
557	}
558
559	/*
560	 * If this packet number is higher than that which we have allocated
561	 * refuse it, unless urgent.
562	 */
563	if (SSEQ_GT(si->si_seq, cb->s_alo)) {
564		if (si->si_cc & SPX_SP) {
565			spxstat.spxs_rcvwinprobe++;
566			return (1);
567		} else
568			spxstat.spxs_rcvpackafterwin++;
569		if (si->si_cc & SPX_OB) {
570			if (SSEQ_GT(si->si_seq, cb->s_alo + 60)) {
571				m_freem(dtom(si));
572				return (0);
573			} /* else queue this packet; */
574		} else {
575#ifdef BROKEN
576			/*
577			 * XXXRW: This is broken on at least one count:
578			 * spx_close() will free the ipxp and related parts,
579			 * which are then touched by spx_input() after the
580			 * return from spx_reass().
581			 */
582			/*struct socket *so = cb->s_ipxpcb->ipxp_socket;
583			if (so->so_state && SS_NOFDREF) {
584				spx_close(cb);
585			} else
586				       would crash system*/
587#endif
588			spx_istat.notyet++;
589			m_freem(dtom(si));
590			return (0);
591		}
592	}
593
594	/*
595	 * If this is a system packet, we don't need to queue it up, and
596	 * won't update acknowledge #.
597	 */
598	if (si->si_cc & SPX_SP)
599		return (1);
600
601	/*
602	 * We have already seen this packet, so drop.
603	 */
604	if (SSEQ_LT(si->si_seq, cb->s_ack)) {
605		spx_istat.bdreas++;
606		spxstat.spxs_rcvduppack++;
607		if (si->si_seq == cb->s_ack - 1)
608			spx_istat.lstdup++;
609		return (1);
610	}
611
612	/*
613	 * Loop through all packets queued up to insert in appropriate
614	 * sequence.
615	 */
616	for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) {
617		if (si->si_seq == SI(q)->si_seq) {
618			spxstat.spxs_rcvduppack++;
619			return (1);
620		}
621		if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) {
622			spxstat.spxs_rcvoopack++;
623			break;
624		}
625	}
626	insque(si, q->si_prev);
627	/*
628	 * If this packet is urgent, inform process
629	 */
630	if (si->si_cc & SPX_OB) {
631		cb->s_iobc = ((char *)si)[1 + sizeof(*si)];
632		sohasoutofband(so);
633		cb->s_oobflags |= SF_IOOB;
634	}
635present:
636#define SPINC sizeof(struct spxhdr)
637	SOCKBUF_LOCK(&so->so_rcv);
638
639	/*
640	 * Loop through all packets queued up to update acknowledge number,
641	 * and present all acknowledged data to user; if in packet interface
642	 * mode, show packet headers.
643	 */
644	for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) {
645		  if (SI(q)->si_seq == cb->s_ack) {
646			cb->s_ack++;
647			m = dtom(q);
648			if (SI(q)->si_cc & SPX_OB) {
649				cb->s_oobflags &= ~SF_IOOB;
650				if (so->so_rcv.sb_cc)
651					so->so_oobmark = so->so_rcv.sb_cc;
652				else
653					so->so_rcv.sb_state |= SBS_RCVATMARK;
654			}
655			q = q->si_prev;
656			remque(q->si_next);
657			wakeup = 1;
658			spxstat.spxs_rcvpack++;
659#ifdef SF_NEWCALL
660			if (cb->s_flags2 & SF_NEWCALL) {
661				struct spxhdr *sp = mtod(m, struct spxhdr *);
662				u_char dt = sp->spx_dt;
663				spx_newchecks[4]++;
664				if (dt != cb->s_rhdr.spx_dt) {
665					struct mbuf *mm =
666					   m_getclr(M_DONTWAIT, MT_CONTROL);
667					spx_newchecks[0]++;
668					if (mm != NULL) {
669						u_short *s =
670							mtod(mm, u_short *);
671						cb->s_rhdr.spx_dt = dt;
672						mm->m_len = 5; /*XXX*/
673						s[0] = 5;
674						s[1] = 1;
675						*(u_char *)(&s[2]) = dt;
676						sbappend_locked(&so->so_rcv, mm);
677					}
678				}
679				if (sp->spx_cc & SPX_OB) {
680					MCHTYPE(m, MT_OOBDATA);
681					spx_newchecks[1]++;
682					so->so_oobmark = 0;
683					so->so_rcv.sb_state &= ~SBS_RCVATMARK;
684				}
685				if (packetp == 0) {
686					m->m_data += SPINC;
687					m->m_len -= SPINC;
688					m->m_pkthdr.len -= SPINC;
689				}
690				if ((sp->spx_cc & SPX_EM) || packetp) {
691					sbappendrecord_locked(&so->so_rcv, m);
692					spx_newchecks[9]++;
693				} else
694					sbappend_locked(&so->so_rcv, m);
695			} else
696#endif
697			if (packetp)
698				sbappendrecord_locked(&so->so_rcv, m);
699			else {
700				cb->s_rhdr = *mtod(m, struct spxhdr *);
701				m->m_data += SPINC;
702				m->m_len -= SPINC;
703				m->m_pkthdr.len -= SPINC;
704				sbappend_locked(&so->so_rcv, m);
705			}
706		  } else
707			break;
708	}
709	if (wakeup)
710		sorwakeup_locked(so);
711	else
712		SOCKBUF_UNLOCK(&so->so_rcv);
713	return (0);
714}
715
716void
717spx_ctlinput(int cmd, struct sockaddr *arg_as_sa, void *dummy)
718{
719
720	/* Currently, nothing. */
721}
722
723static int
724spx_output(struct spxpcb *cb, struct mbuf *m0)
725{
726	struct socket *so = cb->s_ipxpcb->ipxp_socket;
727	struct mbuf *m;
728	struct spx *si = NULL;
729	struct sockbuf *sb = &so->so_snd;
730	int len = 0, win, rcv_win;
731	short span, off, recordp = 0;
732	u_short alo;
733	int error = 0, sendalot;
734#ifdef notdef
735	int idle;
736#endif
737	struct mbuf *mprev;
738
739	IPX_LOCK_ASSERT(cb->s_ipxpcb);
740
741	if (m0 != NULL) {
742		int mtu = cb->s_mtu;
743		int datalen;
744
745		/*
746		 * Make sure that packet isn't too big.
747		 */
748		for (m = m0; m != NULL; m = m->m_next) {
749			mprev = m;
750			len += m->m_len;
751			if (m->m_flags & M_EOR)
752				recordp = 1;
753		}
754		datalen = (cb->s_flags & SF_HO) ?
755				len - sizeof(struct spxhdr) : len;
756		if (datalen > mtu) {
757			if (cb->s_flags & SF_PI) {
758				m_freem(m0);
759				return (EMSGSIZE);
760			} else {
761				int oldEM = cb->s_cc & SPX_EM;
762
763				cb->s_cc &= ~SPX_EM;
764				while (len > mtu) {
765					m = m_copym(m0, 0, mtu, M_DONTWAIT);
766					if (m == NULL) {
767					    cb->s_cc |= oldEM;
768					    m_freem(m0);
769					    return (ENOBUFS);
770					}
771					if (cb->s_flags & SF_NEWCALL) {
772					    struct mbuf *mm = m;
773					    spx_newchecks[7]++;
774					    while (mm != NULL) {
775						mm->m_flags &= ~M_EOR;
776						mm = mm->m_next;
777					    }
778					}
779					error = spx_output(cb, m);
780					if (error) {
781						cb->s_cc |= oldEM;
782						m_freem(m0);
783						return (error);
784					}
785					m_adj(m0, mtu);
786					len -= mtu;
787				}
788				cb->s_cc |= oldEM;
789			}
790		}
791
792		/*
793		 * Force length even, by adding a "garbage byte" if
794		 * necessary.
795		 */
796		if (len & 1) {
797			m = mprev;
798			if (M_TRAILINGSPACE(m) >= 1)
799				m->m_len++;
800			else {
801				struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
802
803				if (m1 == NULL) {
804					m_freem(m0);
805					return (ENOBUFS);
806				}
807				m1->m_len = 1;
808				*(mtod(m1, u_char *)) = 0;
809				m->m_next = m1;
810			}
811		}
812		m = m_gethdr(M_DONTWAIT, MT_DATA);
813		if (m == NULL) {
814			m_freem(m0);
815			return (ENOBUFS);
816		}
817
818		/*
819		 * Fill in mbuf with extended SP header and addresses and
820		 * length put into network format.
821		 */
822		MH_ALIGN(m, sizeof(struct spx));
823		m->m_len = sizeof(struct spx);
824		m->m_next = m0;
825		si = mtod(m, struct spx *);
826		si->si_i = *cb->s_ipx;
827		si->si_s = cb->s_shdr;
828		if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) {
829			struct spxhdr *sh;
830			if (m0->m_len < sizeof(*sh)) {
831				if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) {
832					m_free(m);
833					m_freem(m0);
834					return (EINVAL);
835				}
836				m->m_next = m0;
837			}
838			sh = mtod(m0, struct spxhdr *);
839			si->si_dt = sh->spx_dt;
840			si->si_cc |= sh->spx_cc & SPX_EM;
841			m0->m_len -= sizeof(*sh);
842			m0->m_data += sizeof(*sh);
843			len -= sizeof(*sh);
844		}
845		len += sizeof(*si);
846		if ((cb->s_flags2 & SF_NEWCALL) && recordp) {
847			si->si_cc |= SPX_EM;
848			spx_newchecks[8]++;
849		}
850		if (cb->s_oobflags & SF_SOOB) {
851			/*
852			 * Per jqj@cornell: Make sure OB packets convey
853			 * exactly 1 byte.  If the packet is 1 byte or
854			 * larger, we have already guaranted there to be at
855			 * least one garbage byte for the checksum, and extra
856			 * bytes shouldn't hurt!
857			 */
858			if (len > sizeof(*si)) {
859				si->si_cc |= SPX_OB;
860				len = (1 + sizeof(*si));
861			}
862		}
863		si->si_len = htons((u_short)len);
864		m->m_pkthdr.len = ((len - 1) | 1) + 1;
865
866		/*
867		 * Queue stuff up for output.
868		 */
869		sbappendrecord(sb, m);
870		cb->s_seq++;
871	}
872#ifdef notdef
873	idle = (cb->s_smax == (cb->s_rack - 1));
874#endif
875again:
876	sendalot = 0;
877	off = cb->s_snxt - cb->s_rack;
878	win = min(cb->s_swnd, (cb->s_cwnd / CUNIT));
879
880	/*
881	 * If in persist timeout with window of 0, send a probe.  Otherwise,
882	 * if window is small but nonzero and timer expired, send what we can
883	 * and go into transmit state.
884	 */
885	if (cb->s_force == 1 + SPXT_PERSIST) {
886		if (win != 0) {
887			cb->s_timer[SPXT_PERSIST] = 0;
888			cb->s_rxtshift = 0;
889		}
890	}
891	span = cb->s_seq - cb->s_rack;
892	len = min(span, win) - off;
893
894	if (len < 0) {
895		/*
896		 * Window shrank after we went into it.  If window shrank to
897		 * 0, cancel pending restransmission and pull s_snxt back to
898		 * (closed) window.  We will enter persist state below.  If
899		 * the widndow didn't close completely, just wait for an ACK.
900		 */
901		len = 0;
902		if (win == 0) {
903			cb->s_timer[SPXT_REXMT] = 0;
904			cb->s_snxt = cb->s_rack;
905		}
906	}
907	if (len > 1)
908		sendalot = 1;
909	rcv_win = sbspace(&so->so_rcv);
910
911	/*
912	 * Send if we owe peer an ACK.
913	 */
914	if (cb->s_oobflags & SF_SOOB) {
915		/*
916		 * Must transmit this out of band packet.
917		 */
918		cb->s_oobflags &= ~ SF_SOOB;
919		sendalot = 1;
920		spxstat.spxs_sndurg++;
921		goto found;
922	}
923	if (cb->s_flags & SF_ACKNOW)
924		goto send;
925	if (cb->s_state < TCPS_ESTABLISHED)
926		goto send;
927
928	/*
929	 * Silly window can't happen in spx.  Code from TCP deleted.
930	 */
931	if (len)
932		goto send;
933
934	/*
935	 * Compare available window to amount of window known to peer (as
936	 * advertised window less next expected input.)  If the difference is
937	 * at least two packets or at least 35% of the mximum possible
938	 * window, then want to send a window update to peer.
939	 */
940	if (rcv_win > 0) {
941		u_short delta =  1 + cb->s_alo - cb->s_ack;
942		int adv = rcv_win - (delta * cb->s_mtu);
943
944		if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) ||
945		    (100 * adv / so->so_rcv.sb_hiwat >= 35)) {
946			spxstat.spxs_sndwinup++;
947			cb->s_flags |= SF_ACKNOW;
948			goto send;
949		}
950
951	}
952
953	/*
954	 * Many comments from tcp_output.c are appropriate here including ...
955	 * If send window is too small, there is data to transmit, and no
956	 * retransmit or persist is pending, then go to persist state.  If
957	 * nothing happens soon, send when timer expires: if window is
958	 * nonzero, transmit what we can, otherwise send a probe.
959	 */
960	if (so->so_snd.sb_cc && cb->s_timer[SPXT_REXMT] == 0 &&
961	    cb->s_timer[SPXT_PERSIST] == 0) {
962		cb->s_rxtshift = 0;
963		spx_setpersist(cb);
964	}
965
966	/*
967	 * No reason to send a packet, just return.
968	 */
969	cb->s_outx = 1;
970	return (0);
971
972send:
973	/*
974	 * Find requested packet.
975	 */
976	si = 0;
977	if (len > 0) {
978		cb->s_want = cb->s_snxt;
979		for (m = sb->sb_mb; m != NULL; m = m->m_act) {
980			si = mtod(m, struct spx *);
981			if (SSEQ_LEQ(cb->s_snxt, si->si_seq))
982				break;
983		}
984	found:
985		if (si != NULL) {
986			if (si->si_seq == cb->s_snxt)
987					cb->s_snxt++;
988				else
989					spxstat.spxs_sndvoid++, si = 0;
990		}
991	}
992
993	/*
994	 * Update window.
995	 */
996	if (rcv_win < 0)
997		rcv_win = 0;
998	alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu));
999	if (SSEQ_LT(alo, cb->s_alo))
1000		alo = cb->s_alo;
1001
1002	if (si != NULL) {
1003		/*
1004		 * Must make a copy of this packet for ipx_output to monkey
1005		 * with.
1006		 */
1007		m = m_copy(dtom(si), 0, (int)M_COPYALL);
1008		if (m == NULL)
1009			return (ENOBUFS);
1010		si = mtod(m, struct spx *);
1011		if (SSEQ_LT(si->si_seq, cb->s_smax))
1012			spxstat.spxs_sndrexmitpack++;
1013		else
1014			spxstat.spxs_sndpack++;
1015	} else if (cb->s_force || cb->s_flags & SF_ACKNOW) {
1016		/*
1017		 * Must send an acknowledgement or a probe.
1018		 */
1019		if (cb->s_force)
1020			spxstat.spxs_sndprobe++;
1021		if (cb->s_flags & SF_ACKNOW)
1022			spxstat.spxs_sndacks++;
1023		m = m_gethdr(M_DONTWAIT, MT_DATA);
1024		if (m == NULL)
1025			return (ENOBUFS);
1026
1027		/*
1028		 * Fill in mbuf with extended SP header and addresses and
1029		 * length put into network format.
1030		 */
1031		MH_ALIGN(m, sizeof(struct spx));
1032		m->m_len = sizeof(*si);
1033		m->m_pkthdr.len = sizeof(*si);
1034		si = mtod(m, struct spx *);
1035		si->si_i = *cb->s_ipx;
1036		si->si_s = cb->s_shdr;
1037		si->si_seq = cb->s_smax + 1;
1038		si->si_len = htons(sizeof(*si));
1039		si->si_cc |= SPX_SP;
1040	} else {
1041		cb->s_outx = 3;
1042		if (so->so_options & SO_DEBUG || traceallspxs)
1043			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
1044		return (0);
1045	}
1046	/*
1047	 * Stuff checksum and output datagram.
1048	 */
1049	if ((si->si_cc & SPX_SP) == 0) {
1050		if (cb->s_force != (1 + SPXT_PERSIST) ||
1051		    cb->s_timer[SPXT_PERSIST] == 0) {
1052			/*
1053			 * If this is a new packet and we are not currently
1054			 * timing anything, time this one.
1055			 */
1056			if (SSEQ_LT(cb->s_smax, si->si_seq)) {
1057				cb->s_smax = si->si_seq;
1058				if (cb->s_rtt == 0) {
1059					spxstat.spxs_segstimed++;
1060					cb->s_rtseq = si->si_seq;
1061					cb->s_rtt = 1;
1062				}
1063			}
1064
1065			/*
1066			 * Set rexmt timer if not currently set, initial
1067			 * value for retransmit timer is smoothed round-trip
1068			 * time + 2 * round-trip time variance.  Initialize
1069			 * shift counter which is used for backoff of
1070			 * retransmit time.
1071			 */
1072			if (cb->s_timer[SPXT_REXMT] == 0 &&
1073			    cb->s_snxt != cb->s_rack) {
1074				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1075				if (cb->s_timer[SPXT_PERSIST]) {
1076					cb->s_timer[SPXT_PERSIST] = 0;
1077					cb->s_rxtshift = 0;
1078				}
1079			}
1080		} else if (SSEQ_LT(cb->s_smax, si->si_seq))
1081			cb->s_smax = si->si_seq;
1082	} else if (cb->s_state < TCPS_ESTABLISHED) {
1083		if (cb->s_rtt == 0)
1084			cb->s_rtt = 1; /* Time initial handshake */
1085		if (cb->s_timer[SPXT_REXMT] == 0)
1086			cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1087	}
1088
1089	/*
1090	 * Do not request acks when we ack their data packets or when we do a
1091	 * gratuitous window update.
1092	 */
1093	if (((si->si_cc & SPX_SP) == 0) || cb->s_force)
1094		si->si_cc |= SPX_SA;
1095	si->si_seq = htons(si->si_seq);
1096	si->si_alo = htons(alo);
1097	si->si_ack = htons(cb->s_ack);
1098
1099	if (ipxcksum)
1100		si->si_sum = ipx_cksum(m, ntohs(si->si_len));
1101	else
1102		si->si_sum = 0xffff;
1103
1104	cb->s_outx = 4;
1105	if (so->so_options & SO_DEBUG || traceallspxs)
1106		spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
1107
1108	if (so->so_options & SO_DONTROUTE)
1109		error = ipx_outputfl(m, NULL, IPX_ROUTETOIF);
1110	else
1111		error = ipx_outputfl(m, &cb->s_ipxpcb->ipxp_route, 0);
1112	if (error)
1113		return (error);
1114	spxstat.spxs_sndtotal++;
1115
1116	/*
1117	 * Data sent (as far as we can tell).  If this advertises a larger
1118	 * window than any other segment, then remember the size of the
1119	 * advertized window.  Any pending ACK has now been sent.
1120	 */
1121	cb->s_force = 0;
1122	cb->s_flags &= ~(SF_ACKNOW|SF_DELACK);
1123	if (SSEQ_GT(alo, cb->s_alo))
1124		cb->s_alo = alo;
1125	if (sendalot)
1126		goto again;
1127	cb->s_outx = 5;
1128	return (0);
1129}
1130
1131static int spx_do_persist_panics = 0;
1132
1133static void
1134spx_setpersist(struct spxpcb *cb)
1135{
1136	int t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
1137
1138	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1139
1140	if (cb->s_timer[SPXT_REXMT] && spx_do_persist_panics)
1141		panic("spx_output REXMT");
1142
1143	/*
1144	 * Start/restart persistance timer.
1145	 */
1146	SPXT_RANGESET(cb->s_timer[SPXT_PERSIST],
1147	    t*spx_backoff[cb->s_rxtshift],
1148	    SPXTV_PERSMIN, SPXTV_PERSMAX);
1149	if (cb->s_rxtshift < SPX_MAXRXTSHIFT)
1150		cb->s_rxtshift++;
1151}
1152
1153int
1154spx_ctloutput(struct socket *so, struct sockopt *sopt)
1155{
1156	struct spxhdr spxhdr;
1157	struct ipxpcb *ipxp;
1158	struct spxpcb *cb;
1159	int mask, error;
1160	short soptval;
1161	u_short usoptval;
1162	int optval;
1163
1164	ipxp = sotoipxpcb(so);
1165	KASSERT(ipxp != NULL, ("spx_ctloutput: ipxp == NULL"));
1166
1167	/*
1168	 * This will have to be changed when we do more general stacking of
1169	 * protocols.
1170	 */
1171	if (sopt->sopt_level != IPXPROTO_SPX)
1172		return (ipx_ctloutput(so, sopt));
1173
1174	IPX_LOCK(ipxp);
1175	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1176		IPX_UNLOCK(ipxp);
1177		return (ECONNRESET);
1178	}
1179
1180	IPX_LOCK(ipxp);
1181	cb = ipxtospxpcb(ipxp);
1182	KASSERT(cb != NULL, ("spx_ctloutput: cb == NULL"));
1183
1184	error = 0;
1185	switch (sopt->sopt_dir) {
1186	case SOPT_GET:
1187		switch (sopt->sopt_name) {
1188		case SO_HEADERS_ON_INPUT:
1189			mask = SF_HI;
1190			goto get_flags;
1191
1192		case SO_HEADERS_ON_OUTPUT:
1193			mask = SF_HO;
1194		get_flags:
1195			soptval = cb->s_flags & mask;
1196			IPX_UNLOCK(ipxp);
1197			error = sooptcopyout(sopt, &soptval,
1198			    sizeof(soptval));
1199			break;
1200
1201		case SO_MTU:
1202			usoptval = cb->s_mtu;
1203			IPX_UNLOCK(ipxp);
1204			error = sooptcopyout(sopt, &usoptval,
1205			    sizeof(usoptval));
1206			break;
1207
1208		case SO_LAST_HEADER:
1209			spxhdr = cb->s_rhdr;
1210			IPX_UNLOCK(ipxp);
1211			error = sooptcopyout(sopt, &spxhdr, sizeof(spxhdr));
1212			break;
1213
1214		case SO_DEFAULT_HEADERS:
1215			spxhdr = cb->s_shdr;
1216			IPX_UNLOCK(ipxp);
1217			error = sooptcopyout(sopt, &spxhdr, sizeof(spxhdr));
1218			break;
1219
1220		default:
1221			IPX_UNLOCK(ipxp);
1222			error = ENOPROTOOPT;
1223		}
1224		break;
1225
1226	case SOPT_SET:
1227		/*
1228		 * XXX Why are these shorts on get and ints on set?  That
1229		 * doesn't make any sense...
1230		 *
1231		 * XXXRW: Note, when we re-acquire the ipxp lock, we should
1232		 * re-check that it's not dropped.
1233		 */
1234		IPX_UNLOCK(ipxp);
1235		switch (sopt->sopt_name) {
1236		case SO_HEADERS_ON_INPUT:
1237			mask = SF_HI;
1238			goto set_head;
1239
1240		case SO_HEADERS_ON_OUTPUT:
1241			mask = SF_HO;
1242		set_head:
1243			error = sooptcopyin(sopt, &optval, sizeof optval,
1244					    sizeof optval);
1245			if (error)
1246				break;
1247
1248			IPX_LOCK(ipxp);
1249			if (cb->s_flags & SF_PI) {
1250				if (optval)
1251					cb->s_flags |= mask;
1252				else
1253					cb->s_flags &= ~mask;
1254			} else error = EINVAL;
1255			IPX_UNLOCK(ipxp);
1256			break;
1257
1258		case SO_MTU:
1259			error = sooptcopyin(sopt, &usoptval, sizeof usoptval,
1260					    sizeof usoptval);
1261			if (error)
1262				break;
1263			/* Unlocked write. */
1264			cb->s_mtu = usoptval;
1265			break;
1266
1267#ifdef SF_NEWCALL
1268		case SO_NEWCALL:
1269			error = sooptcopyin(sopt, &optval, sizeof optval,
1270					    sizeof optval);
1271			if (error)
1272				break;
1273			IPX_LOCK(ipxp);
1274			if (optval) {
1275				cb->s_flags2 |= SF_NEWCALL;
1276				spx_newchecks[5]++;
1277			} else {
1278				cb->s_flags2 &= ~SF_NEWCALL;
1279				spx_newchecks[6]++;
1280			}
1281			IPX_UNLOCK(ipxp);
1282			break;
1283#endif
1284
1285		case SO_DEFAULT_HEADERS:
1286			{
1287				struct spxhdr sp;
1288
1289				error = sooptcopyin(sopt, &sp, sizeof sp,
1290						    sizeof sp);
1291				if (error)
1292					break;
1293				IPX_LOCK(ipxp);
1294				cb->s_dt = sp.spx_dt;
1295				cb->s_cc = sp.spx_cc & SPX_EM;
1296				IPX_UNLOCK(ipxp);
1297			}
1298			break;
1299
1300		default:
1301			error = ENOPROTOOPT;
1302		}
1303		break;
1304
1305	default:
1306		panic("spx_ctloutput: bad socket option direction");
1307	}
1308	return (error);
1309}
1310
1311static void
1312spx_usr_abort(struct socket *so)
1313{
1314	struct ipxpcb *ipxp;
1315	struct spxpcb *cb;
1316
1317	ipxp = sotoipxpcb(so);
1318	KASSERT(ipxp != NULL, ("spx_usr_abort: ipxp == NULL"));
1319
1320	cb = ipxtospxpcb(ipxp);
1321	KASSERT(cb != NULL, ("spx_usr_abort: cb == NULL"));
1322
1323	IPX_LIST_LOCK();
1324	IPX_LOCK(ipxp);
1325	spx_drop(cb, ECONNABORTED);
1326	IPX_UNLOCK(ipxp);
1327	IPX_LIST_UNLOCK();
1328}
1329
1330/*
1331 * Accept a connection.  Essentially all the work is done at higher levels;
1332 * just return the address of the peer, storing through addr.
1333 */
1334static int
1335spx_accept(struct socket *so, struct sockaddr **nam)
1336{
1337	struct ipxpcb *ipxp;
1338	struct sockaddr_ipx *sipx, ssipx;
1339
1340	ipxp = sotoipxpcb(so);
1341	KASSERT(ipxp != NULL, ("spx_accept: ipxp == NULL"));
1342
1343	sipx = &ssipx;
1344	bzero(sipx, sizeof *sipx);
1345	sipx->sipx_len = sizeof *sipx;
1346	sipx->sipx_family = AF_IPX;
1347	IPX_LOCK(ipxp);
1348	sipx->sipx_addr = ipxp->ipxp_faddr;
1349	IPX_UNLOCK(ipxp);
1350	*nam = sodupsockaddr((struct sockaddr *)sipx, M_WAITOK);
1351	return (0);
1352}
1353
1354static int
1355spx_attach(struct socket *so, int proto, struct thread *td)
1356{
1357	struct ipxpcb *ipxp;
1358	struct spxpcb *cb;
1359	struct mbuf *mm;
1360	struct sockbuf *sb;
1361	int error;
1362
1363	ipxp = sotoipxpcb(so);
1364	KASSERT(ipxp == NULL, ("spx_attach: ipxp != NULL"));
1365
1366	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
1367		error = soreserve(so, (u_long) 3072, (u_long) 3072);
1368		if (error)
1369			return (error);
1370	}
1371
1372	MALLOC(cb, struct spxpcb *, sizeof *cb, M_PCB, M_NOWAIT | M_ZERO);
1373	if (cb == NULL)
1374		return (ENOBUFS);
1375	mm = m_getclr(M_DONTWAIT, MT_DATA);
1376	if (mm == NULL) {
1377		FREE(cb, M_PCB);
1378		return (ENOBUFS);
1379	}
1380
1381	IPX_LIST_LOCK();
1382	error = ipx_pcballoc(so, &ipxpcb_list, td);
1383	if (error) {
1384		IPX_LIST_UNLOCK();
1385		m_free(mm);
1386		FREE(cb, M_PCB);
1387		return (error);
1388	}
1389	ipxp = sotoipxpcb(so);
1390	ipxp->ipxp_flags |= IPXP_SPX;
1391
1392	cb->s_ipx = mtod(mm, struct ipx *);
1393	cb->s_state = TCPS_LISTEN;
1394	cb->s_smax = -1;
1395	cb->s_swl1 = -1;
1396	cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q;
1397	cb->s_ipxpcb = ipxp;
1398	cb->s_mtu = 576 - sizeof(struct spx);
1399	sb = &so->so_snd;
1400	cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu;
1401	cb->s_ssthresh = cb->s_cwnd;
1402	cb->s_cwmx = sbspace(sb) * CUNIT / (2 * sizeof(struct spx));
1403	/*
1404	 * Above is recomputed when connecting to account for changed
1405	 * buffering or mtu's.
1406	 */
1407	cb->s_rtt = SPXTV_SRTTBASE;
1408	cb->s_rttvar = SPXTV_SRTTDFLT << 2;
1409	SPXT_RANGESET(cb->s_rxtcur,
1410	    ((SPXTV_SRTTBASE >> 2) + (SPXTV_SRTTDFLT << 2)) >> 1,
1411	    SPXTV_MIN, SPXTV_REXMTMAX);
1412	ipxp->ipxp_pcb = (caddr_t)cb;
1413	IPX_LIST_UNLOCK();
1414	return (0);
1415}
1416
1417static void
1418spx_pcbdetach(struct ipxpcb *ipxp)
1419{
1420	struct spxpcb *cb;
1421	struct spx_q *s;
1422	struct mbuf *m;
1423
1424	IPX_LOCK_ASSERT(ipxp);
1425
1426	cb = ipxtospxpcb(ipxp);
1427	KASSERT(cb != NULL, ("spx_pcbdetach: cb == NULL"));
1428
1429	s = cb->s_q.si_next;
1430	while (s != &(cb->s_q)) {
1431		s = s->si_next;
1432		remque(s);
1433		m = dtom(s);
1434		m_freem(m);
1435	}
1436	m_free(dtom(cb->s_ipx));
1437	FREE(cb, M_PCB);
1438	ipxp->ipxp_pcb = NULL;
1439}
1440
1441static int
1442spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
1443{
1444	struct ipxpcb *ipxp;
1445	int error;
1446
1447	ipxp = sotoipxpcb(so);
1448	KASSERT(ipxp != NULL, ("spx_bind: ipxp == NULL"));
1449
1450	IPX_LIST_LOCK();
1451	IPX_LOCK(ipxp);
1452	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1453		error = EINVAL;
1454		goto out;
1455	}
1456	error = ipx_pcbbind(ipxp, nam, td);
1457out:
1458	IPX_UNLOCK(ipxp);
1459	IPX_LIST_UNLOCK();
1460	return (error);
1461}
1462
1463static void
1464spx_usr_close(struct socket *so)
1465{
1466	struct ipxpcb *ipxp;
1467	struct spxpcb *cb;
1468
1469	ipxp = sotoipxpcb(so);
1470	KASSERT(ipxp != NULL, ("spx_usr_close: ipxp == NULL"));
1471
1472	cb = ipxtospxpcb(ipxp);
1473	KASSERT(cb != NULL, ("spx_usr_close: cb == NULL"));
1474
1475	IPX_LIST_LOCK();
1476	IPX_LOCK(ipxp);
1477	if (cb->s_state > TCPS_LISTEN)
1478		spx_disconnect(cb);
1479	else
1480		spx_close(cb);
1481	IPX_UNLOCK(ipxp);
1482	IPX_LIST_UNLOCK();
1483}
1484
1485/*
1486 * Initiate connection to peer.  Enter SYN_SENT state, and mark socket as
1487 * connecting.  Start keep-alive timer, setup prototype header, send initial
1488 * system packet requesting connection.
1489 */
1490static int
1491spx_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
1492{
1493	struct ipxpcb *ipxp;
1494	struct spxpcb *cb;
1495	int error;
1496
1497	ipxp = sotoipxpcb(so);
1498	KASSERT(ipxp != NULL, ("spx_connect: ipxp == NULL"));
1499
1500	cb = ipxtospxpcb(ipxp);
1501	KASSERT(cb != NULL, ("spx_connect: cb == NULL"));
1502
1503	IPX_LIST_LOCK();
1504	IPX_LOCK(ipxp);
1505	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1506		error = EINVAL;
1507		goto spx_connect_end;
1508	}
1509	if (ipxp->ipxp_lport == 0) {
1510		error = ipx_pcbbind(ipxp, NULL, td);
1511		if (error)
1512			goto spx_connect_end;
1513	}
1514	error = ipx_pcbconnect(ipxp, nam, td);
1515	if (error)
1516		goto spx_connect_end;
1517	soisconnecting(so);
1518	spxstat.spxs_connattempt++;
1519	cb->s_state = TCPS_SYN_SENT;
1520	cb->s_did = 0;
1521	spx_template(cb);
1522	cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
1523	cb->s_force = 1 + SPXTV_KEEP;
1524	/*
1525	 * Other party is required to respond to the port I send from, but he
1526	 * is not required to answer from where I am sending to, so allow
1527	 * wildcarding.  Original port I am sending to is still saved in
1528	 * cb->s_dport.
1529	 */
1530	ipxp->ipxp_fport = 0;
1531	error = spx_output(cb, NULL);
1532spx_connect_end:
1533	IPX_UNLOCK(ipxp);
1534	IPX_LIST_UNLOCK();
1535	return (error);
1536}
1537
1538static void
1539spx_detach(struct socket *so)
1540{
1541	struct ipxpcb *ipxp;
1542	struct spxpcb *cb;
1543
1544	/*
1545	 * XXXRW: Should assert appropriately detached.
1546	 */
1547	ipxp = sotoipxpcb(so);
1548	KASSERT(ipxp != NULL, ("spx_detach: ipxp == NULL"));
1549
1550	cb = ipxtospxpcb(ipxp);
1551	KASSERT(cb != NULL, ("spx_detach: cb == NULL"));
1552
1553	IPX_LIST_LOCK();
1554	IPX_LOCK(ipxp);
1555	spx_pcbdetach(ipxp);
1556	ipx_pcbfree(ipxp);
1557	IPX_LIST_UNLOCK();
1558}
1559
1560/*
1561 * We may decide later to implement connection closing handshaking at the spx
1562 * level optionally.  Here is the hook to do it:
1563 */
1564static int
1565spx_usr_disconnect(struct socket *so)
1566{
1567	struct ipxpcb *ipxp;
1568	struct spxpcb *cb;
1569	int error;
1570
1571	ipxp = sotoipxpcb(so);
1572	KASSERT(ipxp != NULL, ("spx_usr_disconnect: ipxp == NULL"));
1573
1574	cb = ipxtospxpcb(ipxp);
1575	KASSERT(cb != NULL, ("spx_usr_disconnect: cb == NULL"));
1576
1577	IPX_LIST_LOCK();
1578	IPX_LOCK(ipxp);
1579	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1580		error = EINVAL;
1581		goto out;
1582	}
1583	spx_disconnect(cb);
1584	error = 0;
1585out:
1586	IPX_UNLOCK(ipxp);
1587	IPX_LIST_UNLOCK();
1588	return (error);
1589}
1590
1591static int
1592spx_listen(struct socket *so, int backlog, struct thread *td)
1593{
1594	int error;
1595	struct ipxpcb *ipxp;
1596	struct spxpcb *cb;
1597
1598	error = 0;
1599	ipxp = sotoipxpcb(so);
1600	KASSERT(ipxp != NULL, ("spx_listen: ipxp == NULL"));
1601
1602	cb = ipxtospxpcb(ipxp);
1603	KASSERT(cb != NULL, ("spx_listen: cb == NULL"));
1604
1605	IPX_LIST_LOCK();
1606	IPX_LOCK(ipxp);
1607	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1608		error = EINVAL;
1609		goto out;
1610	}
1611	SOCK_LOCK(so);
1612	error = solisten_proto_check(so);
1613	if (error == 0 && ipxp->ipxp_lport == 0)
1614		error = ipx_pcbbind(ipxp, NULL, td);
1615	if (error == 0) {
1616		cb->s_state = TCPS_LISTEN;
1617		solisten_proto(so, backlog);
1618	}
1619	SOCK_UNLOCK(so);
1620out:
1621	IPX_UNLOCK(ipxp);
1622	IPX_LIST_UNLOCK();
1623	return (error);
1624}
1625
1626/*
1627 * After a receive, possibly send acknowledgment updating allocation.
1628 */
1629static int
1630spx_rcvd(struct socket *so, int flags)
1631{
1632	struct ipxpcb *ipxp;
1633	struct spxpcb *cb;
1634	int error;
1635
1636	ipxp = sotoipxpcb(so);
1637	KASSERT(ipxp != NULL, ("spx_rcvd: ipxp == NULL"));
1638
1639	cb = ipxtospxpcb(ipxp);
1640	KASSERT(cb != NULL, ("spx_rcvd: cb == NULL"));
1641
1642	IPX_LOCK(ipxp);
1643	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1644		error = EINVAL;
1645		goto out;
1646	}
1647	cb->s_flags |= SF_RVD;
1648	spx_output(cb, NULL);
1649	cb->s_flags &= ~SF_RVD;
1650	error = 0;
1651out:
1652	IPX_UNLOCK(ipxp);
1653	return (error);
1654}
1655
1656static int
1657spx_rcvoob(struct socket *so, struct mbuf *m, int flags)
1658{
1659	struct ipxpcb *ipxp;
1660	struct spxpcb *cb;
1661	int error;
1662
1663	ipxp = sotoipxpcb(so);
1664	KASSERT(ipxp != NULL, ("spx_rcvoob: ipxp == NULL"));
1665
1666	cb = ipxtospxpcb(ipxp);
1667	KASSERT(cb != NULL, ("spx_rcvoob: cb == NULL"));
1668
1669	IPX_LOCK(ipxp);
1670	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1671		error = EINVAL;
1672		goto out;
1673	}
1674	SOCKBUF_LOCK(&so->so_rcv);
1675	if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark ||
1676	    (so->so_rcv.sb_state & SBS_RCVATMARK)) {
1677		SOCKBUF_UNLOCK(&so->so_rcv);
1678		m->m_len = 1;
1679		*mtod(m, caddr_t) = cb->s_iobc;
1680		error = 0;
1681		goto out;
1682	}
1683	SOCKBUF_UNLOCK(&so->so_rcv);
1684	error = EINVAL;
1685out:
1686	IPX_UNLOCK(ipxp);
1687	return (error);
1688}
1689
1690static int
1691spx_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1692    struct mbuf *controlp, struct thread *td)
1693{
1694	struct ipxpcb *ipxp;
1695	struct spxpcb *cb;
1696	int error;
1697
1698	ipxp = sotoipxpcb(so);
1699	KASSERT(ipxp != NULL, ("spx_send: ipxp == NULL"));
1700
1701	cb = ipxtospxpcb(ipxp);
1702	KASSERT(cb != NULL, ("spx_send: cb == NULL"));
1703
1704	error = 0;
1705	IPX_LOCK(ipxp);
1706	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1707		error = ECONNRESET;
1708		goto spx_send_end;
1709	}
1710	if (flags & PRUS_OOB) {
1711		if (sbspace(&so->so_snd) < -512) {
1712			error = ENOBUFS;
1713			goto spx_send_end;
1714		}
1715		cb->s_oobflags |= SF_SOOB;
1716	}
1717	if (controlp != NULL) {
1718		u_short *p = mtod(controlp, u_short *);
1719		spx_newchecks[2]++;
1720		if ((p[0] == 5) && (p[1] == 1)) { /* XXXX, for testing */
1721			cb->s_shdr.spx_dt = *(u_char *)(&p[2]);
1722			spx_newchecks[3]++;
1723		}
1724		m_freem(controlp);
1725	}
1726	controlp = NULL;
1727	error = spx_output(cb, m);
1728	m = NULL;
1729spx_send_end:
1730	IPX_UNLOCK(ipxp);
1731	if (controlp != NULL)
1732		m_freem(controlp);
1733	if (m != NULL)
1734		m_freem(m);
1735	return (error);
1736}
1737
1738static int
1739spx_shutdown(struct socket *so)
1740{
1741	struct ipxpcb *ipxp;
1742	struct spxpcb *cb;
1743	int error;
1744
1745	ipxp = sotoipxpcb(so);
1746	KASSERT(ipxp != NULL, ("spx_shutdown: ipxp == NULL"));
1747
1748	cb = ipxtospxpcb(ipxp);
1749	KASSERT(cb != NULL, ("spx_shutdown: cb == NULL"));
1750
1751	socantsendmore(so);
1752	IPX_LIST_LOCK();
1753	IPX_LOCK(ipxp);
1754	if (ipxp->ipxp_flags & IPXP_DROPPED) {
1755		error = EINVAL;
1756		goto out;
1757	}
1758	spx_usrclosed(cb);
1759	error = 0;
1760out:
1761	IPX_UNLOCK(ipxp);
1762	IPX_LIST_UNLOCK();
1763	return (error);
1764}
1765
1766static int
1767spx_sp_attach(struct socket *so, int proto, struct thread *td)
1768{
1769	struct ipxpcb *ipxp;
1770	struct spxpcb *cb;
1771	int error;
1772
1773	KASSERT(so->so_pcb == NULL, ("spx_sp_attach: so_pcb != NULL"));
1774
1775	error = spx_attach(so, proto, td);
1776	if (error)
1777		return (error);
1778
1779	ipxp = sotoipxpcb(so);
1780	KASSERT(ipxp != NULL, ("spx_sp_attach: ipxp == NULL"));
1781
1782	cb = ipxtospxpcb(ipxp);
1783	KASSERT(cb != NULL, ("spx_sp_attach: cb == NULL"));
1784
1785	IPX_LOCK(ipxp);
1786	cb->s_flags |= (SF_HI | SF_HO | SF_PI);
1787	IPX_UNLOCK(ipxp);
1788	return (0);
1789}
1790
1791/*
1792 * Create template to be used to send spx packets on a connection.  Called
1793 * after host entry created, fills in a skeletal spx header (choosing
1794 * connection id), minimizing the amount of work necessary when the
1795 * connection is used.
1796 */
1797static void
1798spx_template(struct spxpcb *cb)
1799{
1800	struct ipxpcb *ipxp = cb->s_ipxpcb;
1801	struct ipx *ipx = cb->s_ipx;
1802	struct sockbuf *sb = &(ipxp->ipxp_socket->so_snd);
1803
1804	IPX_LOCK_ASSERT(ipxp);
1805
1806	ipx->ipx_pt = IPXPROTO_SPX;
1807	ipx->ipx_sna = ipxp->ipxp_laddr;
1808	ipx->ipx_dna = ipxp->ipxp_faddr;
1809	SPX_LOCK();
1810	cb->s_sid = htons(spx_iss);
1811	spx_iss += SPX_ISSINCR/2;
1812	SPX_UNLOCK();
1813	cb->s_alo = 1;
1814	cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu;
1815	/* Try to expand fast to full complement of large packets. */
1816	cb->s_ssthresh = cb->s_cwnd;
1817	cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spx));
1818	/* But allow for lots of little packets as well. */
1819	cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd);
1820}
1821
1822/*
1823 * Close a SPIP control block.  Wake up any sleepers.  We used to free any
1824 * queued packets and cb->s_ipx here, but now we defer that until the pcb is
1825 * discarded.
1826 */
1827void
1828spx_close(struct spxpcb *cb)
1829{
1830	struct ipxpcb *ipxp = cb->s_ipxpcb;
1831	struct socket *so = ipxp->ipxp_socket;
1832
1833	KASSERT(ipxp != NULL, ("spx_close: ipxp == NULL"));
1834	IPX_LIST_LOCK_ASSERT();
1835	IPX_LOCK_ASSERT(ipxp);
1836
1837	ipxp->ipxp_flags |= IPXP_DROPPED;
1838	soisdisconnected(so);
1839	spxstat.spxs_closed++;
1840}
1841
1842/*
1843 * Someday we may do level 3 handshaking to close a connection or send a
1844 * xerox style error.  For now, just close.  cb will always be invalid after
1845 * this call.
1846 */
1847static void
1848spx_usrclosed(struct spxpcb *cb)
1849{
1850
1851	IPX_LIST_LOCK_ASSERT();
1852	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1853
1854	spx_close(cb);
1855}
1856
1857/*
1858 * cb will always be invalid after this call.
1859 */
1860static void
1861spx_disconnect(struct spxpcb *cb)
1862{
1863
1864	IPX_LIST_LOCK_ASSERT();
1865	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1866
1867	spx_close(cb);
1868}
1869
1870/*
1871 * Drop connection, reporting the specified error.  cb will always be invalid
1872 * after this call.
1873 */
1874static void
1875spx_drop(struct spxpcb *cb, int errno)
1876{
1877	struct socket *so = cb->s_ipxpcb->ipxp_socket;
1878
1879	IPX_LIST_LOCK_ASSERT();
1880	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1881
1882	/*
1883	 * Someday, in the xerox world we will generate error protocol
1884	 * packets announcing that the socket has gone away.
1885	 */
1886	if (TCPS_HAVERCVDSYN(cb->s_state)) {
1887		spxstat.spxs_drops++;
1888		cb->s_state = TCPS_CLOSED;
1889		/*tcp_output(cb);*/
1890	} else
1891		spxstat.spxs_conndrops++;
1892	so->so_error = errno;
1893	spx_close(cb);
1894}
1895
1896/*
1897 * Fast timeout routine for processing delayed acks.
1898 */
1899void
1900spx_fasttimo(void)
1901{
1902	struct ipxpcb *ipxp;
1903	struct spxpcb *cb;
1904
1905	IPX_LIST_LOCK();
1906	LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) {
1907		IPX_LOCK(ipxp);
1908		if (!(ipxp->ipxp_flags & IPXP_SPX) ||
1909		    (ipxp->ipxp_flags & IPXP_DROPPED)) {
1910			IPX_UNLOCK(ipxp);
1911			continue;
1912		}
1913		cb = ipxtospxpcb(ipxp);
1914		if (cb->s_flags & SF_DELACK) {
1915			cb->s_flags &= ~SF_DELACK;
1916			cb->s_flags |= SF_ACKNOW;
1917			spxstat.spxs_delack++;
1918			spx_output(cb, NULL);
1919		}
1920		IPX_UNLOCK(ipxp);
1921	}
1922	IPX_LIST_UNLOCK();
1923}
1924
1925/*
1926 * spx protocol timeout routine called every 500 ms.  Updates the timers in
1927 * all active pcb's and causes finite state machine actions if timers expire.
1928 */
1929void
1930spx_slowtimo(void)
1931{
1932	struct ipxpcb *ipxp;
1933	struct spxpcb *cb;
1934	int i;
1935
1936	/*
1937	 * Search through tcb's and update active timers.  Once, timers could
1938	 * free ipxp's, but now we do that only when detaching a socket.
1939	 */
1940	IPX_LIST_LOCK();
1941	LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) {
1942		IPX_LOCK(ipxp);
1943		if (!(ipxp->ipxp_flags & IPXP_SPX) ||
1944		    (ipxp->ipxp_flags & IPXP_DROPPED)) {
1945			IPX_UNLOCK(ipxp);
1946			continue;
1947		}
1948
1949		cb = (struct spxpcb *)ipxp->ipxp_pcb;
1950		KASSERT(cb != NULL, ("spx_slowtimo: cb == NULL"));
1951		for (i = 0; i < SPXT_NTIMERS; i++) {
1952			if (cb->s_timer[i] && --cb->s_timer[i] == 0) {
1953				spx_timers(cb, i);
1954				if (ipxp->ipxp_flags & IPXP_DROPPED)
1955					break;
1956			}
1957		}
1958		if (!(ipxp->ipxp_flags & IPXP_DROPPED)) {
1959			cb->s_idle++;
1960			if (cb->s_rtt)
1961				cb->s_rtt++;
1962		}
1963		IPX_UNLOCK(ipxp);
1964	}
1965	IPX_LIST_UNLOCK();
1966	SPX_LOCK();
1967	spx_iss += SPX_ISSINCR/PR_SLOWHZ;		/* increment iss */
1968	SPX_UNLOCK();
1969}
1970
1971/*
1972 * SPX timer processing.
1973 */
1974static void
1975spx_timers(struct spxpcb *cb, int timer)
1976{
1977	long rexmt;
1978	int win;
1979
1980	IPX_LIST_LOCK_ASSERT();
1981	IPX_LOCK_ASSERT(cb->s_ipxpcb);
1982
1983	cb->s_force = 1 + timer;
1984	switch (timer) {
1985	case SPXT_2MSL:
1986		/*
1987		 * 2 MSL timeout in shutdown went off.  TCP deletes
1988		 * connection control block.
1989		 */
1990		printf("spx: SPXT_2MSL went off for no reason\n");
1991		cb->s_timer[timer] = 0;
1992		break;
1993
1994	case SPXT_REXMT:
1995		/*
1996		 * Retransmission timer went off.  Message has not been acked
1997		 * within retransmit interval.  Back off to a longer
1998		 * retransmit interval and retransmit one packet.
1999		 */
2000		if (++cb->s_rxtshift > SPX_MAXRXTSHIFT) {
2001			cb->s_rxtshift = SPX_MAXRXTSHIFT;
2002			spxstat.spxs_timeoutdrop++;
2003			spx_drop(cb, ETIMEDOUT);
2004			break;
2005		}
2006		spxstat.spxs_rexmttimeo++;
2007		rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
2008		rexmt *= spx_backoff[cb->s_rxtshift];
2009		SPXT_RANGESET(cb->s_rxtcur, rexmt, SPXTV_MIN, SPXTV_REXMTMAX);
2010		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
2011
2012		/*
2013		 * If we have backed off fairly far, our srtt estimate is
2014		 * probably bogus.  Clobber it so we'll take the next rtt
2015		 * measurement as our srtt; move the current srtt into rttvar
2016		 * to keep the current retransmit times until then.
2017		 */
2018		if (cb->s_rxtshift > SPX_MAXRXTSHIFT / 4 ) {
2019			cb->s_rttvar += (cb->s_srtt >> 2);
2020			cb->s_srtt = 0;
2021		}
2022		cb->s_snxt = cb->s_rack;
2023
2024		/*
2025		 * If timing a packet, stop the timer.
2026		 */
2027		cb->s_rtt = 0;
2028
2029		/*
2030		 * See very long discussion in tcp_timer.c about congestion
2031		 * window and sstrhesh.
2032		 */
2033		win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2;
2034		if (win < 2)
2035			win = 2;
2036		cb->s_cwnd = CUNIT;
2037		cb->s_ssthresh = win * CUNIT;
2038		spx_output(cb, NULL);
2039		break;
2040
2041	case SPXT_PERSIST:
2042		/*
2043		 * Persistance timer into zero window.  Force a probe to be
2044		 * sent.
2045		 */
2046		spxstat.spxs_persisttimeo++;
2047		spx_setpersist(cb);
2048		spx_output(cb, NULL);
2049		break;
2050
2051	case SPXT_KEEP:
2052		/*
2053		 * Keep-alive timer went off; send something or drop
2054		 * connection if idle for too long.
2055		 */
2056		spxstat.spxs_keeptimeo++;
2057		if (cb->s_state < TCPS_ESTABLISHED)
2058			goto dropit;
2059		if (cb->s_ipxpcb->ipxp_socket->so_options & SO_KEEPALIVE) {
2060		    	if (cb->s_idle >= SPXTV_MAXIDLE)
2061				goto dropit;
2062			spxstat.spxs_keepprobe++;
2063			spx_output(cb, NULL);
2064		} else
2065			cb->s_idle = 0;
2066		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
2067		break;
2068
2069	dropit:
2070		spxstat.spxs_keepdrops++;
2071		spx_drop(cb, ETIMEDOUT);
2072		break;
2073
2074	default:
2075		panic("spx_timers: unknown timer %d", timer);
2076	}
2077}
2078