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