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