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