spx_usrreq.c revision 21673
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 * $FreeBSD: head/sys/netipx/spx_usrreq.c 21673 1997-01-14 07:20:47Z jkh $
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/queue.h>
45#include <sys/socket.h>
46#include <sys/socketvar.h>
47#include <sys/errno.h>
48
49#include <net/if.h>
50#include <net/route.h>
51#include <netinet/tcp_fsm.h>
52
53#include <netipx/ipx.h>
54#include <netipx/ipx_pcb.h>
55#include <netipx/ipx_var.h>
56#include <netipx/ipx_error.h>
57#include <netipx/spx.h>
58#include <netipx/spx_timer.h>
59#include <netipx/spx_var.h>
60#include <netipx/spx_debug.h>
61
62/*
63 * SPX protocol implementation.
64 */
65
66struct spx spx_savesi;
67int traceallspxs = 0;
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_as_sa, dummy)
588	int cmd;
589	struct sockaddr *arg_as_sa;	/* XXX should be swapped with dummy */
590	void *dummy;
591{
592	caddr_t arg = (/* XXX */ caddr_t)arg_as_sa;
593	struct ipx_addr *na;
594	struct ipx_errp *errp = (struct ipx_errp *)arg;
595	struct ipxpcb *ipxp;
596	struct sockaddr_ipx *sipx;
597	int type;
598
599	if (cmd < 0 || cmd > PRC_NCMDS)
600		return;
601	type = IPX_ERR_UNREACH_HOST;
602
603	switch (cmd) {
604
605	case PRC_ROUTEDEAD:
606		return;
607
608	case PRC_IFDOWN:
609	case PRC_HOSTDEAD:
610	case PRC_HOSTUNREACH:
611		sipx = (struct sockaddr_ipx *)arg;
612		if (sipx->sipx_family != AF_IPX)
613			return;
614		na = &sipx->sipx_addr;
615		break;
616
617	default:
618		errp = (struct ipx_errp *)arg;
619		na = &errp->ipx_err_ipx.ipx_dna;
620		type = errp->ipx_err_num;
621		type = ntohs((u_short)type);
622		break;
623	}
624	switch (type) {
625
626	case IPX_ERR_UNREACH_HOST:
627		ipx_pcbnotify(na, (int)ipxctlerrmap[cmd], spx_abort, (long)0);
628		break;
629
630	case IPX_ERR_TOO_BIG:
631	case IPX_ERR_NOSOCK:
632		ipxp = ipx_pcblookup(na, errp->ipx_err_ipx.ipx_sna.x_port,
633			IPX_WILDCARD);
634		if (ipxp) {
635			if(ipxp->ipxp_pcb)
636				(void) spx_drop((struct spxpcb *)ipxp->ipxp_pcb,
637						(int)ipxctlerrmap[cmd]);
638			else
639				(void) ipx_drop(ipxp, (int)ipxctlerrmap[cmd]);
640		}
641		break;
642
643	case IPX_ERR_FULLUP:
644		ipx_pcbnotify(na, 0, spx_quench, (long)0);
645		break;
646	}
647}
648/*
649 * When a source quench is received, close congestion window
650 * to one packet.  We will gradually open it again as we proceed.
651 */
652void
653spx_quench(ipxp)
654	struct ipxpcb *ipxp;
655{
656	struct spxpcb *cb = ipxtospxpcb(ipxp);
657
658	if (cb)
659		cb->s_cwnd = CUNIT;
660}
661
662#ifdef notdef
663int
664spx_fixmtu(ipxp)
665register struct ipxpcb *ipxp;
666{
667	register struct spxpcb *cb = (struct spxpcb *)(ipxp->ipxp_pcb);
668	register struct mbuf *m;
669	register struct spx *si;
670	struct ipx_errp *ep;
671	struct sockbuf *sb;
672	int badseq, len;
673	struct mbuf *firstbad, *m0;
674
675	if (cb) {
676		/*
677		 * The notification that we have sent
678		 * too much is bad news -- we will
679		 * have to go through queued up so far
680		 * splitting ones which are too big and
681		 * reassigning sequence numbers and checksums.
682		 * we should then retransmit all packets from
683		 * one above the offending packet to the last one
684		 * we had sent (or our allocation)
685		 * then the offending one so that the any queued
686		 * data at our destination will be discarded.
687		 */
688		 ep = (struct ipx_errp *)ipxp->ipxp_notify_param;
689		 sb = &ipxp->ipxp_socket->so_snd;
690		 cb->s_mtu = ep->ipx_err_param;
691		 badseq = SI(&ep->ipx_err_ipx)->si_seq;
692		 for (m = sb->sb_mb; m; m = m->m_act) {
693			si = mtod(m, struct spx *);
694			if (si->si_seq == badseq)
695				break;
696		 }
697		 if (m == 0) return;
698		 firstbad = m;
699		 /*for (;;) {*/
700			/* calculate length */
701			for (m0 = m, len = 0; m ; m = m->m_next)
702				len += m->m_len;
703			if (len > cb->s_mtu) {
704			}
705		/* FINISH THIS
706		} */
707	}
708}
709#endif
710
711int
712spx_output(cb, m0)
713	register struct spxpcb *cb;
714	struct mbuf *m0;
715{
716	struct socket *so = cb->s_ipxpcb->ipxp_socket;
717	register struct mbuf *m;
718	register struct spx *si = (struct spx *) 0;
719	register struct sockbuf *sb = &so->so_snd;
720	int len = 0, win, rcv_win;
721	short span, off, recordp = 0;
722	u_short alo;
723	int error = 0, sendalot;
724#ifdef notdef
725	int idle;
726#endif
727	struct mbuf *mprev;
728
729	if (m0) {
730		int mtu = cb->s_mtu;
731		int datalen;
732		/*
733		 * Make sure that packet isn't too big.
734		 */
735		for (m = m0; m ; m = m->m_next) {
736			mprev = m;
737			len += m->m_len;
738			if (m->m_flags & M_EOR)
739				recordp = 1;
740		}
741		datalen = (cb->s_flags & SF_HO) ?
742				len - sizeof (struct spxhdr) : len;
743		if (datalen > mtu) {
744			if (cb->s_flags & SF_PI) {
745				m_freem(m0);
746				return (EMSGSIZE);
747			} else {
748				int oldEM = cb->s_cc & SPX_EM;
749
750				cb->s_cc &= ~SPX_EM;
751				while (len > mtu) {
752					/*
753					 * Here we are only being called
754					 * from usrreq(), so it is OK to
755					 * block.
756					 */
757					m = m_copym(m0, 0, mtu, M_WAIT);
758					if (cb->s_flags & SF_NEWCALL) {
759					    struct mbuf *mm = m;
760					    spx_newchecks[7]++;
761					    while (mm) {
762						mm->m_flags &= ~M_EOR;
763						mm = mm->m_next;
764					    }
765					}
766					error = spx_output(cb, m);
767					if (error) {
768						cb->s_cc |= oldEM;
769						m_freem(m0);
770						return(error);
771					}
772					m_adj(m0, mtu);
773					len -= mtu;
774				}
775				cb->s_cc |= oldEM;
776			}
777		}
778		/*
779		 * Force length even, by adding a "garbage byte" if
780		 * necessary.
781		 */
782		if (len & 1) {
783			m = mprev;
784			if (M_TRAILINGSPACE(m) >= 1)
785				m->m_len++;
786			else {
787				struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
788
789				if (m1 == 0) {
790					m_freem(m0);
791					return (ENOBUFS);
792				}
793				m1->m_len = 1;
794				*(mtod(m1, u_char *)) = 0;
795				m->m_next = m1;
796			}
797		}
798		m = m_gethdr(M_DONTWAIT, MT_HEADER);
799		if (m == 0) {
800			m_freem(m0);
801			return (ENOBUFS);
802		}
803		/*
804		 * Fill in mbuf with extended SP header
805		 * and addresses and length put into network format.
806		 */
807		MH_ALIGN(m, sizeof (struct spx));
808		m->m_len = sizeof (struct spx);
809		m->m_next = m0;
810		si = mtod(m, struct spx *);
811		si->si_i = *cb->s_ipx;
812		si->si_s = cb->s_shdr;
813		if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) {
814			register struct spxhdr *sh;
815			if (m0->m_len < sizeof (*sh)) {
816				if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) {
817					(void) m_free(m);
818					m_freem(m0);
819					return (EINVAL);
820				}
821				m->m_next = m0;
822			}
823			sh = mtod(m0, struct spxhdr *);
824			si->si_dt = sh->spx_dt;
825			si->si_cc |= sh->spx_cc & SPX_EM;
826			m0->m_len -= sizeof (*sh);
827			m0->m_data += sizeof (*sh);
828			len -= sizeof (*sh);
829		}
830		len += sizeof(*si);
831		if ((cb->s_flags2 & SF_NEWCALL) && recordp) {
832			si->si_cc  |= SPX_EM;
833			spx_newchecks[8]++;
834		}
835		if (cb->s_oobflags & SF_SOOB) {
836			/*
837			 * Per jqj@cornell:
838			 * make sure OB packets convey exactly 1 byte.
839			 * If the packet is 1 byte or larger, we
840			 * have already guaranted there to be at least
841			 * one garbage byte for the checksum, and
842			 * extra bytes shouldn't hurt!
843			 */
844			if (len > sizeof(*si)) {
845				si->si_cc |= SPX_OB;
846				len = (1 + sizeof(*si));
847			}
848		}
849		si->si_len = htons((u_short)len);
850		m->m_pkthdr.len = ((len - 1) | 1) + 1;
851		/*
852		 * queue stuff up for output
853		 */
854		sbappendrecord(sb, m);
855		cb->s_seq++;
856	}
857#ifdef notdef
858	idle = (cb->s_smax == (cb->s_rack - 1));
859#endif
860again:
861	sendalot = 0;
862	off = cb->s_snxt - cb->s_rack;
863	win = min(cb->s_swnd, (cb->s_cwnd/CUNIT));
864
865	/*
866	 * If in persist timeout with window of 0, send a probe.
867	 * Otherwise, if window is small but nonzero
868	 * and timer expired, send what we can and go into
869	 * transmit state.
870	 */
871	if (cb->s_force == 1 + SPXT_PERSIST) {
872		if (win != 0) {
873			cb->s_timer[SPXT_PERSIST] = 0;
874			cb->s_rxtshift = 0;
875		}
876	}
877	span = cb->s_seq - cb->s_rack;
878	len = min(span, win) - off;
879
880	if (len < 0) {
881		/*
882		 * Window shrank after we went into it.
883		 * If window shrank to 0, cancel pending
884		 * restransmission and pull s_snxt back
885		 * to (closed) window.  We will enter persist
886		 * state below.  If the widndow didn't close completely,
887		 * just wait for an ACK.
888		 */
889		len = 0;
890		if (win == 0) {
891			cb->s_timer[SPXT_REXMT] = 0;
892			cb->s_snxt = cb->s_rack;
893		}
894	}
895	if (len > 1)
896		sendalot = 1;
897	rcv_win = sbspace(&so->so_rcv);
898
899	/*
900	 * Send if we owe peer an ACK.
901	 */
902	if (cb->s_oobflags & SF_SOOB) {
903		/*
904		 * must transmit this out of band packet
905		 */
906		cb->s_oobflags &= ~ SF_SOOB;
907		sendalot = 1;
908		spxstat.spxs_sndurg++;
909		goto found;
910	}
911	if (cb->s_flags & SF_ACKNOW)
912		goto send;
913	if (cb->s_state < TCPS_ESTABLISHED)
914		goto send;
915	/*
916	 * Silly window can't happen in spx.
917	 * Code from tcp deleted.
918	 */
919	if (len)
920		goto send;
921	/*
922	 * Compare available window to amount of window
923	 * known to peer (as advertised window less
924	 * next expected input.)  If the difference is at least two
925	 * packets or at least 35% of the mximum possible window,
926	 * then want to send a window update to peer.
927	 */
928	if (rcv_win > 0) {
929		u_short delta =  1 + cb->s_alo - cb->s_ack;
930		int adv = rcv_win - (delta * cb->s_mtu);
931
932		if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) ||
933		    (100 * adv / so->so_rcv.sb_hiwat >= 35)) {
934			spxstat.spxs_sndwinup++;
935			cb->s_flags |= SF_ACKNOW;
936			goto send;
937		}
938
939	}
940	/*
941	 * Many comments from tcp_output.c are appropriate here
942	 * including . . .
943	 * If send window is too small, there is data to transmit, and no
944	 * retransmit or persist is pending, then go to persist state.
945	 * If nothing happens soon, send when timer expires:
946	 * if window is nonzero, transmit what we can,
947	 * otherwise send a probe.
948	 */
949	if (so->so_snd.sb_cc && cb->s_timer[SPXT_REXMT] == 0 &&
950		cb->s_timer[SPXT_PERSIST] == 0) {
951			cb->s_rxtshift = 0;
952			spx_setpersist(cb);
953	}
954	/*
955	 * No reason to send a packet, just return.
956	 */
957	cb->s_outx = 1;
958	return (0);
959
960send:
961	/*
962	 * Find requested packet.
963	 */
964	si = 0;
965	if (len > 0) {
966		cb->s_want = cb->s_snxt;
967		for (m = sb->sb_mb; m; m = m->m_act) {
968			si = mtod(m, struct spx *);
969			if (SSEQ_LEQ(cb->s_snxt, si->si_seq))
970				break;
971		}
972	found:
973		if (si) {
974			if (si->si_seq == cb->s_snxt)
975					cb->s_snxt++;
976				else
977					spxstat.spxs_sndvoid++, si = 0;
978		}
979	}
980	/*
981	 * update window
982	 */
983	if (rcv_win < 0)
984		rcv_win = 0;
985	alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu));
986	if (SSEQ_LT(alo, cb->s_alo))
987		alo = cb->s_alo;
988
989	if (si) {
990		/*
991		 * must make a copy of this packet for
992		 * ipx_output to monkey with
993		 */
994		m = m_copy(dtom(si), 0, (int)M_COPYALL);
995		if (m == NULL) {
996			return (ENOBUFS);
997		}
998		si = mtod(m, struct spx *);
999		if (SSEQ_LT(si->si_seq, cb->s_smax))
1000			spxstat.spxs_sndrexmitpack++;
1001		else
1002			spxstat.spxs_sndpack++;
1003	} else if (cb->s_force || cb->s_flags & SF_ACKNOW) {
1004		/*
1005		 * Must send an acknowledgement or a probe
1006		 */
1007		if (cb->s_force)
1008			spxstat.spxs_sndprobe++;
1009		if (cb->s_flags & SF_ACKNOW)
1010			spxstat.spxs_sndacks++;
1011		m = m_gethdr(M_DONTWAIT, MT_HEADER);
1012		if (m == 0)
1013			return (ENOBUFS);
1014		/*
1015		 * Fill in mbuf with extended SP header
1016		 * and addresses and length put into network format.
1017		 */
1018		MH_ALIGN(m, sizeof (struct spx));
1019		m->m_len = sizeof (*si);
1020		m->m_pkthdr.len = sizeof (*si);
1021		si = mtod(m, struct spx *);
1022		si->si_i = *cb->s_ipx;
1023		si->si_s = cb->s_shdr;
1024		si->si_seq = cb->s_smax + 1;
1025		si->si_len = htons(sizeof (*si));
1026		si->si_cc |= SPX_SP;
1027	} else {
1028		cb->s_outx = 3;
1029		if (so->so_options & SO_DEBUG || traceallspxs)
1030			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
1031		return (0);
1032	}
1033	/*
1034	 * Stuff checksum and output datagram.
1035	 */
1036	if ((si->si_cc & SPX_SP) == 0) {
1037		if (cb->s_force != (1 + SPXT_PERSIST) ||
1038		    cb->s_timer[SPXT_PERSIST] == 0) {
1039			/*
1040			 * If this is a new packet and we are not currently
1041			 * timing anything, time this one.
1042			 */
1043			if (SSEQ_LT(cb->s_smax, si->si_seq)) {
1044				cb->s_smax = si->si_seq;
1045				if (cb->s_rtt == 0) {
1046					spxstat.spxs_segstimed++;
1047					cb->s_rtseq = si->si_seq;
1048					cb->s_rtt = 1;
1049				}
1050			}
1051			/*
1052			 * Set rexmt timer if not currently set,
1053			 * Initial value for retransmit timer is smoothed
1054			 * round-trip time + 2 * round-trip time variance.
1055			 * Initialize shift counter which is used for backoff
1056			 * of retransmit time.
1057			 */
1058			if (cb->s_timer[SPXT_REXMT] == 0 &&
1059			    cb->s_snxt != cb->s_rack) {
1060				cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1061				if (cb->s_timer[SPXT_PERSIST]) {
1062					cb->s_timer[SPXT_PERSIST] = 0;
1063					cb->s_rxtshift = 0;
1064				}
1065			}
1066		} else if (SSEQ_LT(cb->s_smax, si->si_seq)) {
1067			cb->s_smax = si->si_seq;
1068		}
1069	} else if (cb->s_state < TCPS_ESTABLISHED) {
1070		if (cb->s_rtt == 0)
1071			cb->s_rtt = 1; /* Time initial handshake */
1072		if (cb->s_timer[SPXT_REXMT] == 0)
1073			cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1074	}
1075	{
1076		/*
1077		 * Do not request acks when we ack their data packets or
1078		 * when we do a gratuitous window update.
1079		 */
1080		if (((si->si_cc & SPX_SP) == 0) || cb->s_force)
1081				si->si_cc |= SPX_SA;
1082		si->si_seq = htons(si->si_seq);
1083		si->si_alo = htons(alo);
1084		si->si_ack = htons(cb->s_ack);
1085
1086		if (ipxcksum) {
1087			si->si_sum = 0;
1088			len = ntohs(si->si_len);
1089			if (len & 1)
1090				len++;
1091			si->si_sum = ipx_cksum(m, len);
1092		} else
1093			si->si_sum = 0xffff;
1094
1095		cb->s_outx = 4;
1096		if (so->so_options & SO_DEBUG || traceallspxs)
1097			spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0);
1098
1099		if (so->so_options & SO_DONTROUTE)
1100			error = ipx_outputfl(m, (struct route *)0, IPX_ROUTETOIF);
1101		else
1102			error = ipx_outputfl(m, &cb->s_ipxpcb->ipxp_route, 0);
1103	}
1104	if (error) {
1105		return (error);
1106	}
1107	spxstat.spxs_sndtotal++;
1108	/*
1109	 * Data sent (as far as we can tell).
1110	 * If this advertises a larger window than any other segment,
1111	 * then remember the size of the advertized window.
1112	 * Any pending ACK has now been sent.
1113	 */
1114	cb->s_force = 0;
1115	cb->s_flags &= ~(SF_ACKNOW|SF_DELACK);
1116	if (SSEQ_GT(alo, cb->s_alo))
1117		cb->s_alo = alo;
1118	if (sendalot)
1119		goto again;
1120	cb->s_outx = 5;
1121	return (0);
1122}
1123
1124int spx_do_persist_panics = 0;
1125
1126void
1127spx_setpersist(cb)
1128	register struct spxpcb *cb;
1129{
1130	register t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
1131
1132	if (cb->s_timer[SPXT_REXMT] && spx_do_persist_panics)
1133		panic("spx_output REXMT");
1134	/*
1135	 * Start/restart persistance timer.
1136	 */
1137	SPXT_RANGESET(cb->s_timer[SPXT_PERSIST],
1138	    t*spx_backoff[cb->s_rxtshift],
1139	    SPXTV_PERSMIN, SPXTV_PERSMAX);
1140	if (cb->s_rxtshift < SPX_MAXRXTSHIFT)
1141		cb->s_rxtshift++;
1142}
1143/*ARGSUSED*/
1144int
1145spx_ctloutput(req, so, level, name, value)
1146	int req;
1147	struct socket *so;
1148	int level, name;
1149	struct mbuf **value;
1150{
1151	register struct mbuf *m;
1152	struct ipxpcb *ipxp = sotoipxpcb(so);
1153	register struct spxpcb *cb;
1154	int mask, error = 0;
1155
1156	if (level != IPXPROTO_SPX) {
1157		/* This will have to be changed when we do more general
1158		   stacking of protocols */
1159		return (ipx_ctloutput(req, so, level, name, value));
1160	}
1161	if (ipxp == NULL) {
1162		error = EINVAL;
1163		goto release;
1164	} else
1165		cb = ipxtospxpcb(ipxp);
1166
1167	switch (req) {
1168
1169	case PRCO_GETOPT:
1170		if (value == NULL)
1171			return (EINVAL);
1172		m = m_get(M_DONTWAIT, MT_DATA);
1173		if (m == NULL)
1174			return (ENOBUFS);
1175		switch (name) {
1176
1177		case SO_HEADERS_ON_INPUT:
1178			mask = SF_HI;
1179			goto get_flags;
1180
1181		case SO_HEADERS_ON_OUTPUT:
1182			mask = SF_HO;
1183		get_flags:
1184			m->m_len = sizeof(short);
1185			*mtod(m, short *) = cb->s_flags & mask;
1186			break;
1187
1188		case SO_MTU:
1189			m->m_len = sizeof(u_short);
1190			*mtod(m, short *) = cb->s_mtu;
1191			break;
1192
1193		case SO_LAST_HEADER:
1194			m->m_len = sizeof(struct spxhdr);
1195			*mtod(m, struct spxhdr *) = cb->s_rhdr;
1196			break;
1197
1198		case SO_DEFAULT_HEADERS:
1199			m->m_len = sizeof(struct spx);
1200			*mtod(m, struct spxhdr *) = cb->s_shdr;
1201			break;
1202
1203		default:
1204			error = EINVAL;
1205		}
1206		*value = m;
1207		break;
1208
1209	case PRCO_SETOPT:
1210		if (value == 0 || *value == 0) {
1211			error = EINVAL;
1212			break;
1213		}
1214		switch (name) {
1215			int *ok;
1216
1217		case SO_HEADERS_ON_INPUT:
1218			mask = SF_HI;
1219			goto set_head;
1220
1221		case SO_HEADERS_ON_OUTPUT:
1222			mask = SF_HO;
1223		set_head:
1224			if (cb->s_flags & SF_PI) {
1225				ok = mtod(*value, int *);
1226				if (*ok)
1227					cb->s_flags |= mask;
1228				else
1229					cb->s_flags &= ~mask;
1230			} else error = EINVAL;
1231			break;
1232
1233		case SO_MTU:
1234			cb->s_mtu = *(mtod(*value, u_short *));
1235			break;
1236
1237#ifdef SF_NEWCALL
1238		case SO_NEWCALL:
1239			ok = mtod(*value, int *);
1240			if (*ok) {
1241				cb->s_flags2 |= SF_NEWCALL;
1242				spx_newchecks[5]++;
1243			} else {
1244				cb->s_flags2 &= ~SF_NEWCALL;
1245				spx_newchecks[6]++;
1246			}
1247			break;
1248#endif
1249
1250		case SO_DEFAULT_HEADERS:
1251			{
1252				register struct spxhdr *sp
1253						= mtod(*value, struct spxhdr *);
1254				cb->s_dt = sp->spx_dt;
1255				cb->s_cc = sp->spx_cc & SPX_EM;
1256			}
1257			break;
1258
1259		default:
1260			error = EINVAL;
1261		}
1262		m_freem(*value);
1263		break;
1264	}
1265	release:
1266		return (error);
1267}
1268
1269/*ARGSUSED*/
1270int
1271spx_usrreq(so, req, m, nam, controlp)
1272	struct socket *so;
1273	int req;
1274	struct mbuf *m, *nam, *controlp;
1275{
1276	struct ipxpcb *ipxp = sotoipxpcb(so);
1277	register struct spxpcb *cb = NULL;
1278	int s = splnet();
1279	int error = 0, ostate;
1280	struct mbuf *mm;
1281	register struct sockbuf *sb;
1282
1283	if (req == PRU_CONTROL)
1284                return (ipx_control(so, (int)m, (caddr_t)nam,
1285			(struct ifnet *)controlp));
1286	if (ipxp == NULL) {
1287		if (req != PRU_ATTACH) {
1288			error = EINVAL;
1289			goto release;
1290		}
1291	} else
1292		cb = ipxtospxpcb(ipxp);
1293
1294	ostate = cb ? cb->s_state : 0;
1295
1296	switch (req) {
1297
1298	case PRU_ATTACH:
1299		if (ipxp != NULL) {
1300			error = EISCONN;
1301			break;
1302		}
1303		error = ipx_pcballoc(so, &ipxpcb);
1304		if (error)
1305			break;
1306		if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
1307			error = soreserve(so, (u_long) 3072, (u_long) 3072);
1308			if (error)
1309				break;
1310		}
1311		ipxp = sotoipxpcb(so);
1312
1313		mm = m_getclr(M_DONTWAIT, MT_PCB);
1314		sb = &so->so_snd;
1315
1316		if (mm == NULL) {
1317			error = ENOBUFS;
1318			break;
1319		}
1320		cb = mtod(mm, struct spxpcb *);
1321		mm = m_getclr(M_DONTWAIT, MT_HEADER);
1322		if (mm == NULL) {
1323			(void) m_free(dtom(m));
1324			error = ENOBUFS;
1325			break;
1326		}
1327		cb->s_ipx = mtod(mm, struct ipx *);
1328		cb->s_state = TCPS_LISTEN;
1329		cb->s_smax = -1;
1330		cb->s_swl1 = -1;
1331		cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q;
1332		cb->s_ipxpcb = ipxp;
1333		cb->s_mtu = 576 - sizeof (struct spx);
1334		cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu;
1335		cb->s_ssthresh = cb->s_cwnd;
1336		cb->s_cwmx = sbspace(sb) * CUNIT /
1337				(2 * sizeof (struct spx));
1338		/* Above is recomputed when connecting to account
1339		   for changed buffering or mtu's */
1340		cb->s_rtt = SPXTV_SRTTBASE;
1341		cb->s_rttvar = SPXTV_SRTTDFLT << 2;
1342		SPXT_RANGESET(cb->s_rxtcur,
1343		    ((SPXTV_SRTTBASE >> 2) + (SPXTV_SRTTDFLT << 2)) >> 1,
1344		    SPXTV_MIN, SPXTV_REXMTMAX);
1345		ipxp->ipxp_pcb = (caddr_t) cb;
1346		break;
1347
1348	case PRU_DETACH:
1349		if (ipxp == NULL) {
1350			error = ENOTCONN;
1351			break;
1352		}
1353		if (cb->s_state > TCPS_LISTEN)
1354			cb = spx_disconnect(cb);
1355		else
1356			cb = spx_close(cb);
1357		break;
1358
1359	case PRU_BIND:
1360		error = ipx_pcbbind(ipxp, nam);
1361		break;
1362
1363	case PRU_LISTEN:
1364		if (ipxp->ipxp_lport == 0)
1365			error = ipx_pcbbind(ipxp, (struct mbuf *)0);
1366		if (error == 0)
1367			cb->s_state = TCPS_LISTEN;
1368		break;
1369
1370	/*
1371	 * Initiate connection to peer.
1372	 * Enter SYN_SENT state, and mark socket as connecting.
1373	 * Start keep-alive timer, setup prototype header,
1374	 * Send initial system packet requesting connection.
1375	 */
1376	case PRU_CONNECT:
1377		if (ipxp->ipxp_lport == 0) {
1378			error = ipx_pcbbind(ipxp, (struct mbuf *)0);
1379			if (error)
1380				break;
1381		}
1382		error = ipx_pcbconnect(ipxp, nam);
1383		if (error)
1384			break;
1385		soisconnecting(so);
1386		spxstat.spxs_connattempt++;
1387		cb->s_state = TCPS_SYN_SENT;
1388		cb->s_did = 0;
1389		spx_template(cb);
1390		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
1391		cb->s_force = 1 + SPXTV_KEEP;
1392		/*
1393		 * Other party is required to respond to
1394		 * the port I send from, but he is not
1395		 * required to answer from where I am sending to,
1396		 * so allow wildcarding.
1397		 * original port I am sending to is still saved in
1398		 * cb->s_dport.
1399		 */
1400		ipxp->ipxp_fport = 0;
1401		error = spx_output(cb, (struct mbuf *) 0);
1402		break;
1403
1404	case PRU_CONNECT2:
1405		error = EOPNOTSUPP;
1406		break;
1407
1408	/*
1409	 * We may decide later to implement connection closing
1410	 * handshaking at the spx level optionally.
1411	 * here is the hook to do it:
1412	 */
1413	case PRU_DISCONNECT:
1414		cb = spx_disconnect(cb);
1415		break;
1416
1417	/*
1418	 * Accept a connection.  Essentially all the work is
1419	 * done at higher levels; just return the address
1420	 * of the peer, storing through addr.
1421	 */
1422	case PRU_ACCEPT: {
1423		struct sockaddr_ipx *sipx = mtod(nam, struct sockaddr_ipx *);
1424
1425		nam->m_len = sizeof (struct sockaddr_ipx);
1426		sipx->sipx_family = AF_IPX;
1427		sipx->sipx_addr = ipxp->ipxp_faddr;
1428		break;
1429		}
1430
1431	case PRU_SHUTDOWN:
1432		socantsendmore(so);
1433		cb = spx_usrclosed(cb);
1434		if (cb)
1435			error = spx_output(cb, (struct mbuf *) 0);
1436		break;
1437
1438	/*
1439	 * After a receive, possibly send acknowledgment
1440	 * updating allocation.
1441	 */
1442	case PRU_RCVD:
1443		cb->s_flags |= SF_RVD;
1444		(void) spx_output(cb, (struct mbuf *) 0);
1445		cb->s_flags &= ~SF_RVD;
1446		break;
1447
1448	case PRU_ABORT:
1449		(void) spx_drop(cb, ECONNABORTED);
1450		break;
1451
1452	case PRU_SENSE:
1453	case PRU_CONTROL:
1454		m = NULL;
1455		error = EOPNOTSUPP;
1456		break;
1457
1458	case PRU_RCVOOB:
1459		if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark ||
1460		    (so->so_state & SS_RCVATMARK)) {
1461			m->m_len = 1;
1462			*mtod(m, caddr_t) = cb->s_iobc;
1463			break;
1464		}
1465		error = EINVAL;
1466		break;
1467
1468	case PRU_SENDOOB:
1469		if (sbspace(&so->so_snd) < -512) {
1470			error = ENOBUFS;
1471			break;
1472		}
1473		cb->s_oobflags |= SF_SOOB;
1474		/* fall into */
1475	case PRU_SEND:
1476		if (controlp) {
1477			u_short *p = mtod(controlp, u_short *);
1478			spx_newchecks[2]++;
1479			if ((p[0] == 5) && p[1] == 1) { /* XXXX, for testing */
1480				cb->s_shdr.spx_dt = *(u_char *)(&p[2]);
1481				spx_newchecks[3]++;
1482			}
1483			m_freem(controlp);
1484		}
1485		controlp = NULL;
1486		error = spx_output(cb, m);
1487		m = NULL;
1488		break;
1489
1490	case PRU_SOCKADDR:
1491		ipx_setsockaddr(ipxp, nam);
1492		break;
1493
1494	case PRU_PEERADDR:
1495		ipx_setpeeraddr(ipxp, nam);
1496		break;
1497
1498	case PRU_SLOWTIMO:
1499		cb = spx_timers(cb, (int)nam);
1500		req |= ((int)nam) << 8;
1501		break;
1502
1503	case PRU_FASTTIMO:
1504	case PRU_PROTORCV:
1505	case PRU_PROTOSEND:
1506		error =  EOPNOTSUPP;
1507		break;
1508
1509	default:
1510		panic("spx_usrreq");
1511	}
1512	if (cb && (so->so_options & SO_DEBUG || traceallspxs))
1513		spx_trace(SA_USER, (u_char)ostate, cb, (struct spx *)0, req);
1514release:
1515	if (controlp != NULL)
1516		m_freem(controlp);
1517	if (m != NULL)
1518		m_freem(m);
1519	splx(s);
1520	return (error);
1521}
1522
1523int
1524spx_usrreq_sp(so, req, m, nam, controlp)
1525	struct socket *so;
1526	int req;
1527	struct mbuf *m, *nam, *controlp;
1528{
1529	int error = spx_usrreq(so, req, m, nam, controlp);
1530
1531	if (req == PRU_ATTACH && error == 0) {
1532		struct ipxpcb *ipxp = sotoipxpcb(so);
1533		((struct spxpcb *)ipxp->ipxp_pcb)->s_flags |=
1534					(SF_HI | SF_HO | SF_PI);
1535	}
1536	return (error);
1537}
1538
1539/*
1540 * Create template to be used to send spx packets on a connection.
1541 * Called after host entry created, fills
1542 * in a skeletal spx header (choosing connection id),
1543 * minimizing the amount of work necessary when the connection is used.
1544 */
1545void
1546spx_template(cb)
1547	register struct spxpcb *cb;
1548{
1549	register struct ipxpcb *ipxp = cb->s_ipxpcb;
1550	register struct ipx *ipx = cb->s_ipx;
1551	register struct sockbuf *sb = &(ipxp->ipxp_socket->so_snd);
1552
1553	ipx->ipx_pt = IPXPROTO_SPX;
1554	ipx->ipx_sna = ipxp->ipxp_laddr;
1555	ipx->ipx_dna = ipxp->ipxp_faddr;
1556	cb->s_sid = htons(spx_iss);
1557	spx_iss += SPX_ISSINCR/2;
1558	cb->s_alo = 1;
1559	cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu;
1560	cb->s_ssthresh = cb->s_cwnd; /* Try to expand fast to full complement
1561					of large packets */
1562	cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spx));
1563	cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd);
1564		/* But allow for lots of little packets as well */
1565}
1566
1567/*
1568 * Close a SPIP control block:
1569 *	discard spx control block itself
1570 *	discard ipx protocol control block
1571 *	wake up any sleepers
1572 */
1573struct spxpcb *
1574spx_close(cb)
1575	register struct spxpcb *cb;
1576{
1577	register struct spx_q *s;
1578	struct ipxpcb *ipxp = cb->s_ipxpcb;
1579	struct socket *so = ipxp->ipxp_socket;
1580	register struct mbuf *m;
1581
1582	s = cb->s_q.si_next;
1583	while (s != &(cb->s_q)) {
1584		s = s->si_next;
1585		m = dtom(s->si_prev);
1586		remque(s->si_prev);
1587		m_freem(m);
1588	}
1589	(void) m_free(dtom(cb->s_ipx));
1590	(void) m_free(dtom(cb));
1591	ipxp->ipxp_pcb = 0;
1592	soisdisconnected(so);
1593	ipx_pcbdetach(ipxp);
1594	spxstat.spxs_closed++;
1595	return ((struct spxpcb *)0);
1596}
1597/*
1598 *	Someday we may do level 3 handshaking
1599 *	to close a connection or send a xerox style error.
1600 *	For now, just close.
1601 */
1602struct spxpcb *
1603spx_usrclosed(cb)
1604	register struct spxpcb *cb;
1605{
1606	return (spx_close(cb));
1607}
1608struct spxpcb *
1609spx_disconnect(cb)
1610	register struct spxpcb *cb;
1611{
1612	return (spx_close(cb));
1613}
1614/*
1615 * Drop connection, reporting
1616 * the specified error.
1617 */
1618struct spxpcb *
1619spx_drop(cb, errno)
1620	register struct spxpcb *cb;
1621	int errno;
1622{
1623	struct socket *so = cb->s_ipxpcb->ipxp_socket;
1624
1625	/*
1626	 * someday, in the xerox world
1627	 * we will generate error protocol packets
1628	 * announcing that the socket has gone away.
1629	 */
1630	if (TCPS_HAVERCVDSYN(cb->s_state)) {
1631		spxstat.spxs_drops++;
1632		cb->s_state = TCPS_CLOSED;
1633		/*(void) tcp_output(cb);*/
1634	} else
1635		spxstat.spxs_conndrops++;
1636	so->so_error = errno;
1637	return (spx_close(cb));
1638}
1639
1640void
1641spx_abort(ipxp)
1642	struct ipxpcb *ipxp;
1643{
1644
1645	(void) spx_close((struct spxpcb *)ipxp->ipxp_pcb);
1646}
1647
1648int	spx_backoff[SPX_MAXRXTSHIFT+1] =
1649    { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
1650/*
1651 * Fast timeout routine for processing delayed acks
1652 */
1653void
1654spx_fasttimo()
1655{
1656	register struct ipxpcb *ipxp;
1657	register struct spxpcb *cb;
1658	int s = splnet();
1659
1660	ipxp = ipxpcb.ipxp_next;
1661	if (ipxp)
1662	for (; ipxp != &ipxpcb; ipxp = ipxp->ipxp_next)
1663		if ((cb = (struct spxpcb *)ipxp->ipxp_pcb) &&
1664		    (cb->s_flags & SF_DELACK)) {
1665			cb->s_flags &= ~SF_DELACK;
1666			cb->s_flags |= SF_ACKNOW;
1667			spxstat.spxs_delack++;
1668			(void) spx_output(cb, (struct mbuf *) 0);
1669		}
1670	splx(s);
1671}
1672
1673/*
1674 * spx protocol timeout routine called every 500 ms.
1675 * Updates the timers in all active pcb's and
1676 * causes finite state machine actions if timers expire.
1677 */
1678void
1679spx_slowtimo()
1680{
1681	register struct ipxpcb *ip, *ipnxt;
1682	register struct spxpcb *cb;
1683	int s = splnet();
1684	register int i;
1685
1686	/*
1687	 * Search through tcb's and update active timers.
1688	 */
1689	ip = ipxpcb.ipxp_next;
1690	if (ip == 0) {
1691		splx(s);
1692		return;
1693	}
1694	while (ip != &ipxpcb) {
1695		cb = ipxtospxpcb(ip);
1696		ipnxt = ip->ipxp_next;
1697		if (cb == 0)
1698			goto tpgone;
1699		for (i = 0; i < SPXT_NTIMERS; i++) {
1700			if (cb->s_timer[i] && --cb->s_timer[i] == 0) {
1701				(void) spx_usrreq(cb->s_ipxpcb->ipxp_socket,
1702				    PRU_SLOWTIMO, (struct mbuf *)0,
1703				    (struct mbuf *)i, (struct mbuf *)0);
1704				if (ipnxt->ipxp_prev != ip)
1705					goto tpgone;
1706			}
1707		}
1708		cb->s_idle++;
1709		if (cb->s_rtt)
1710			cb->s_rtt++;
1711tpgone:
1712		ip = ipnxt;
1713	}
1714	spx_iss += SPX_ISSINCR/PR_SLOWHZ;		/* increment iss */
1715	splx(s);
1716}
1717/*
1718 * SPX timer processing.
1719 */
1720struct spxpcb *
1721spx_timers(cb, timer)
1722	register struct spxpcb *cb;
1723	int timer;
1724{
1725	long rexmt;
1726	int win;
1727
1728	cb->s_force = 1 + timer;
1729	switch (timer) {
1730
1731	/*
1732	 * 2 MSL timeout in shutdown went off.  TCP deletes connection
1733	 * control block.
1734	 */
1735	case SPXT_2MSL:
1736		printf("spx: SPXT_2MSL went off for no reason\n");
1737		cb->s_timer[timer] = 0;
1738		break;
1739
1740	/*
1741	 * Retransmission timer went off.  Message has not
1742	 * been acked within retransmit interval.  Back off
1743	 * to a longer retransmit interval and retransmit one packet.
1744	 */
1745	case SPXT_REXMT:
1746		if (++cb->s_rxtshift > SPX_MAXRXTSHIFT) {
1747			cb->s_rxtshift = SPX_MAXRXTSHIFT;
1748			spxstat.spxs_timeoutdrop++;
1749			cb = spx_drop(cb, ETIMEDOUT);
1750			break;
1751		}
1752		spxstat.spxs_rexmttimeo++;
1753		rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1;
1754		rexmt *= spx_backoff[cb->s_rxtshift];
1755		SPXT_RANGESET(cb->s_rxtcur, rexmt, SPXTV_MIN, SPXTV_REXMTMAX);
1756		cb->s_timer[SPXT_REXMT] = cb->s_rxtcur;
1757		/*
1758		 * If we have backed off fairly far, our srtt
1759		 * estimate is probably bogus.  Clobber it
1760		 * so we'll take the next rtt measurement as our srtt;
1761		 * move the current srtt into rttvar to keep the current
1762		 * retransmit times until then.
1763		 */
1764		if (cb->s_rxtshift > SPX_MAXRXTSHIFT / 4 ) {
1765			cb->s_rttvar += (cb->s_srtt >> 2);
1766			cb->s_srtt = 0;
1767		}
1768		cb->s_snxt = cb->s_rack;
1769		/*
1770		 * If timing a packet, stop the timer.
1771		 */
1772		cb->s_rtt = 0;
1773		/*
1774		 * See very long discussion in tcp_timer.c about congestion
1775		 * window and sstrhesh
1776		 */
1777		win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2;
1778		if (win < 2)
1779			win = 2;
1780		cb->s_cwnd = CUNIT;
1781		cb->s_ssthresh = win * CUNIT;
1782		(void) spx_output(cb, (struct mbuf *) 0);
1783		break;
1784
1785	/*
1786	 * Persistance timer into zero window.
1787	 * Force a probe to be sent.
1788	 */
1789	case SPXT_PERSIST:
1790		spxstat.spxs_persisttimeo++;
1791		spx_setpersist(cb);
1792		(void) spx_output(cb, (struct mbuf *) 0);
1793		break;
1794
1795	/*
1796	 * Keep-alive timer went off; send something
1797	 * or drop connection if idle for too long.
1798	 */
1799	case SPXT_KEEP:
1800		spxstat.spxs_keeptimeo++;
1801		if (cb->s_state < TCPS_ESTABLISHED)
1802			goto dropit;
1803		if (cb->s_ipxpcb->ipxp_socket->so_options & SO_KEEPALIVE) {
1804		    	if (cb->s_idle >= SPXTV_MAXIDLE)
1805				goto dropit;
1806			spxstat.spxs_keepprobe++;
1807			(void) spx_output(cb, (struct mbuf *) 0);
1808		} else
1809			cb->s_idle = 0;
1810		cb->s_timer[SPXT_KEEP] = SPXTV_KEEP;
1811		break;
1812	dropit:
1813		spxstat.spxs_keepdrops++;
1814		cb = spx_drop(cb, ETIMEDOUT);
1815		break;
1816	}
1817	return (cb);
1818}
1819