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