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