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