spx_reass.c revision 157068
1139823Simp/*- 2157067Srwatson * Copyright (c) 1984, 1985, 1986, 1987, 1993 3157067Srwatson * The Regents of the University of California. 411819Sjulian * Copyright (c) 1995, Mike Mitchell 5157067Srwatson * Copyright (c) 2004-2006 Robert N. M. Watson 6157067Srwatson * All rights reserved. 711819Sjulian * 811819Sjulian * Redistribution and use in source and binary forms, with or without 911819Sjulian * modification, are permitted provided that the following conditions 1011819Sjulian * are met: 1111819Sjulian * 1. Redistributions of source code must retain the above copyright 1211819Sjulian * notice, this list of conditions and the following disclaimer. 1311819Sjulian * 2. Redistributions in binary form must reproduce the above copyright 1411819Sjulian * notice, this list of conditions and the following disclaimer in the 1511819Sjulian * documentation and/or other materials provided with the distribution. 1611819Sjulian * 3. All advertising materials mentioning features or use of this software 1711819Sjulian * must display the following acknowledgement: 1811819Sjulian * This product includes software developed by the University of 1911819Sjulian * California, Berkeley and its contributors. 2011819Sjulian * 4. Neither the name of the University nor the names of its contributors 2111819Sjulian * may be used to endorse or promote products derived from this software 2211819Sjulian * without specific prior written permission. 2311819Sjulian * 2411819Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2511819Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2611819Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2711819Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2811819Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2911819Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3011819Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3111819Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3211819Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3311819Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3411819Sjulian * SUCH DAMAGE. 3511819Sjulian * 3612057Sjulian * @(#)spx_usrreq.h 3711819Sjulian */ 3811819Sjulian 39116189Sobrien#include <sys/cdefs.h> 40116189Sobrien__FBSDID("$FreeBSD: head/sys/netipx/spx_usrreq.c 157068 2006-03-24 00:22:25Z rwatson $"); 41116189Sobrien 4211819Sjulian#include <sys/param.h> 4376166Smarkm#include <sys/lock.h> 4429024Sbde#include <sys/malloc.h> 4511819Sjulian#include <sys/mbuf.h> 4676166Smarkm#include <sys/mutex.h> 4725345Sjhay#include <sys/proc.h> 4811819Sjulian#include <sys/protosw.h> 4995759Stanimura#include <sys/signalvar.h> 5011819Sjulian#include <sys/socket.h> 5111819Sjulian#include <sys/socketvar.h> 5295759Stanimura#include <sys/sx.h> 5395759Stanimura#include <sys/systm.h> 5411819Sjulian 5511819Sjulian#include <net/route.h> 5611819Sjulian#include <netinet/tcp_fsm.h> 5711819Sjulian 5811819Sjulian#include <netipx/ipx.h> 5911819Sjulian#include <netipx/ipx_pcb.h> 6011819Sjulian#include <netipx/ipx_var.h> 6111819Sjulian#include <netipx/spx.h> 6295759Stanimura#include <netipx/spx_debug.h> 6311819Sjulian#include <netipx/spx_timer.h> 6411819Sjulian#include <netipx/spx_var.h> 6511819Sjulian 6611819Sjulian/* 6711819Sjulian * SPX protocol implementation. 6811819Sjulian */ 6933181Seivindstatic u_short spx_iss; 7033181Seivindstatic u_short spx_newchecks[50]; 7133181Seivindstatic int spx_hardnosed; 7233181Seivindstatic int spx_use_delack = 0; 7333181Seivindstatic int traceallspxs = 0; 7433181Seivindstatic struct spx_istat spx_istat; 75157068Srwatsonstatic int spxrexmtthresh = 3; 7611819Sjulian 7725652Sjhay/* Following was struct spxstat spxstat; */ 78139584Srwatson#ifndef spxstat 7925652Sjhay#define spxstat spx_istat.newstats 80139584Srwatson#endif 8111819Sjulian 82132045Srwatsonstatic const int spx_backoff[SPX_MAXRXTSHIFT+1] = 8325652Sjhay { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 }; 8411819Sjulian 85139931Srwatsonstatic void spx_close(struct spxpcb *cb); 86139931Srwatsonstatic void spx_disconnect(struct spxpcb *cb); 87139931Srwatsonstatic void spx_drop(struct spxpcb *cb, int errno); 8825652Sjhaystatic int spx_output(struct spxpcb *cb, struct mbuf *m0); 8925652Sjhaystatic int spx_reass(struct spxpcb *cb, struct spx *si); 9025652Sjhaystatic void spx_setpersist(struct spxpcb *cb); 9125652Sjhaystatic void spx_template(struct spxpcb *cb); 9225652Sjhaystatic struct spxpcb *spx_timers(struct spxpcb *cb, int timer); 93139931Srwatsonstatic void spx_usrclosed(struct spxpcb *cb); 9425652Sjhay 9524659Sjhaystatic int spx_usr_abort(struct socket *so); 9628270Swollmanstatic int spx_accept(struct socket *so, struct sockaddr **nam); 9783366Sjulianstatic int spx_attach(struct socket *so, int proto, struct thread *td); 9883366Sjulianstatic int spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td); 9928270Swollmanstatic int spx_connect(struct socket *so, struct sockaddr *nam, 10083366Sjulian struct thread *td); 10124659Sjhaystatic int spx_detach(struct socket *so); 10224659Sjhaystatic int spx_usr_disconnect(struct socket *so); 103151888Srwatsonstatic int spx_listen(struct socket *so, int backlog, struct thread *td); 10424659Sjhaystatic int spx_rcvd(struct socket *so, int flags); 10524659Sjhaystatic int spx_rcvoob(struct socket *so, struct mbuf *m, int flags); 10624659Sjhaystatic int spx_send(struct socket *so, int flags, struct mbuf *m, 107139584Srwatson struct sockaddr *addr, struct mbuf *control, 10883366Sjulian struct thread *td); 10924659Sjhaystatic int spx_shutdown(struct socket *so); 11083366Sjulianstatic int spx_sp_attach(struct socket *so, int proto, struct thread *td); 11124659Sjhay 11224659Sjhaystruct pr_usrreqs spx_usrreqs = { 113137386Sphk .pru_abort = spx_usr_abort, 114137386Sphk .pru_accept = spx_accept, 115137386Sphk .pru_attach = spx_attach, 116137386Sphk .pru_bind = spx_bind, 117137386Sphk .pru_connect = spx_connect, 118137386Sphk .pru_control = ipx_control, 119137386Sphk .pru_detach = spx_detach, 120137386Sphk .pru_disconnect = spx_usr_disconnect, 121137386Sphk .pru_listen = spx_listen, 122137386Sphk .pru_peeraddr = ipx_peeraddr, 123137386Sphk .pru_rcvd = spx_rcvd, 124137386Sphk .pru_rcvoob = spx_rcvoob, 125137386Sphk .pru_send = spx_send, 126137386Sphk .pru_shutdown = spx_shutdown, 127137386Sphk .pru_sockaddr = ipx_sockaddr, 12824659Sjhay}; 12924659Sjhay 13024659Sjhaystruct pr_usrreqs spx_usrreq_sps = { 131137386Sphk .pru_abort = spx_usr_abort, 132137386Sphk .pru_accept = spx_accept, 133137386Sphk .pru_attach = spx_sp_attach, 134137386Sphk .pru_bind = spx_bind, 135137386Sphk .pru_connect = spx_connect, 136137386Sphk .pru_control = ipx_control, 137137386Sphk .pru_detach = spx_detach, 138137386Sphk .pru_disconnect = spx_usr_disconnect, 139137386Sphk .pru_listen = spx_listen, 140137386Sphk .pru_peeraddr = ipx_peeraddr, 141137386Sphk .pru_rcvd = spx_rcvd, 142137386Sphk .pru_rcvoob = spx_rcvoob, 143137386Sphk .pru_send = spx_send, 144137386Sphk .pru_shutdown = spx_shutdown, 145137386Sphk .pru_sockaddr = ipx_sockaddr, 14624659Sjhay}; 14724659Sjhay 14811819Sjulianvoid 149157067Srwatsonspx_init(void) 15011819Sjulian{ 15111819Sjulian 15211819Sjulian spx_iss = 1; /* WRONG !! should fish it out of TODR */ 15311819Sjulian} 15411819Sjulian 15511819Sjulianvoid 156157067Srwatsonspx_input(struct mbuf *m, struct ipxpcb *ipxp) 15711819Sjulian{ 158157067Srwatson struct spxpcb *cb; 159157067Srwatson struct spx *si = mtod(m, struct spx *); 160157067Srwatson struct socket *so; 161157051Srwatson struct spx spx_savesi; 16211819Sjulian int dropsocket = 0; 16311819Sjulian short ostate = 0; 16411819Sjulian 16511819Sjulian spxstat.spxs_rcvtotal++; 166139586Srwatson KASSERT(ipxp != NULL, ("spx_input: NULL ipxpcb")); 16711819Sjulian 168139932Srwatson /* 169139932Srwatson * spx_input() assumes that the caller will hold both the pcb list 170139932Srwatson * lock and also the ipxp lock. spx_input() will release both before 171139932Srwatson * returning, and may in fact trade in the ipxp lock for another pcb 172139932Srwatson * lock following sonewconn(). 173139932Srwatson */ 174139932Srwatson IPX_LIST_LOCK_ASSERT(); 175139932Srwatson IPX_LOCK_ASSERT(ipxp); 176139932Srwatson 17711819Sjulian cb = ipxtospxpcb(ipxp); 17825652Sjhay if (cb == NULL) 17925652Sjhay goto bad; 18011819Sjulian 18111819Sjulian if (m->m_len < sizeof(*si)) { 18225652Sjhay if ((m = m_pullup(m, sizeof(*si))) == NULL) { 183139932Srwatson IPX_UNLOCK(ipxp); 184139932Srwatson IPX_LIST_UNLOCK(); 18511819Sjulian spxstat.spxs_rcvshort++; 18611819Sjulian return; 18711819Sjulian } 18811819Sjulian si = mtod(m, struct spx *); 18911819Sjulian } 19011819Sjulian si->si_seq = ntohs(si->si_seq); 19111819Sjulian si->si_ack = ntohs(si->si_ack); 19211819Sjulian si->si_alo = ntohs(si->si_alo); 19311819Sjulian 19411819Sjulian so = ipxp->ipxp_socket; 19511819Sjulian 19611819Sjulian if (so->so_options & SO_DEBUG || traceallspxs) { 19711819Sjulian ostate = cb->s_state; 19811819Sjulian spx_savesi = *si; 19911819Sjulian } 20011819Sjulian if (so->so_options & SO_ACCEPTCONN) { 20111819Sjulian struct spxpcb *ocb = cb; 20211819Sjulian 20311819Sjulian so = sonewconn(so, 0); 20425652Sjhay if (so == NULL) { 20511819Sjulian goto drop; 20611819Sjulian } 20711819Sjulian /* 20811819Sjulian * This is ugly, but .... 20911819Sjulian * 21011819Sjulian * Mark socket as temporary until we're 21111819Sjulian * committed to keeping it. The code at 21211819Sjulian * ``drop'' and ``dropwithreset'' check the 21311819Sjulian * flag dropsocket to see if the temporary 21411819Sjulian * socket created here should be discarded. 21511819Sjulian * We mark the socket as discardable until 21611819Sjulian * we're committed to it below in TCPS_LISTEN. 21711819Sjulian */ 21811819Sjulian dropsocket++; 219139932Srwatson IPX_UNLOCK(ipxp); 22011819Sjulian ipxp = (struct ipxpcb *)so->so_pcb; 221139932Srwatson IPX_LOCK(ipxp); 22211819Sjulian ipxp->ipxp_laddr = si->si_dna; 22311819Sjulian cb = ipxtospxpcb(ipxp); 22411819Sjulian cb->s_mtu = ocb->s_mtu; /* preserve sockopts */ 22511819Sjulian cb->s_flags = ocb->s_flags; /* preserve sockopts */ 22611819Sjulian cb->s_flags2 = ocb->s_flags2; /* preserve sockopts */ 22711819Sjulian cb->s_state = TCPS_LISTEN; 22897658Stanimura } 22911819Sjulian 23011819Sjulian /* 23111819Sjulian * Packet received on connection. 23211819Sjulian * reset idle time and keep-alive timer; 23311819Sjulian */ 23411819Sjulian cb->s_idle = 0; 23511819Sjulian cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 23611819Sjulian 23711819Sjulian switch (cb->s_state) { 23811819Sjulian 23911819Sjulian case TCPS_LISTEN:{ 24028270Swollman struct sockaddr_ipx *sipx, ssipx; 24111819Sjulian struct ipx_addr laddr; 24211819Sjulian 24311819Sjulian /* 24411819Sjulian * If somebody here was carying on a conversation 24511819Sjulian * and went away, and his pen pal thinks he can 24611819Sjulian * still talk, we get the misdirected packet. 24711819Sjulian */ 24811819Sjulian if (spx_hardnosed && (si->si_did != 0 || si->si_seq != 0)) { 24911819Sjulian spx_istat.gonawy++; 25011819Sjulian goto dropwithreset; 25111819Sjulian } 25228270Swollman sipx = &ssipx; 25328270Swollman bzero(sipx, sizeof *sipx); 25411819Sjulian sipx->sipx_len = sizeof(*sipx); 25511819Sjulian sipx->sipx_family = AF_IPX; 25611819Sjulian sipx->sipx_addr = si->si_sna; 25711819Sjulian laddr = ipxp->ipxp_laddr; 25811819Sjulian if (ipx_nullhost(laddr)) 25911819Sjulian ipxp->ipxp_laddr = si->si_dna; 26090361Sjulian if (ipx_pcbconnect(ipxp, (struct sockaddr *)sipx, &thread0)) { 26111819Sjulian ipxp->ipxp_laddr = laddr; 26211819Sjulian spx_istat.noconn++; 26311819Sjulian goto drop; 26411819Sjulian } 26511819Sjulian spx_template(cb); 26611819Sjulian dropsocket = 0; /* committed to socket */ 26711819Sjulian cb->s_did = si->si_sid; 26811819Sjulian cb->s_rack = si->si_ack; 26911819Sjulian cb->s_ralo = si->si_alo; 27011819Sjulian#define THREEWAYSHAKE 27111819Sjulian#ifdef THREEWAYSHAKE 27211819Sjulian cb->s_state = TCPS_SYN_RECEIVED; 27311819Sjulian cb->s_force = 1 + SPXT_KEEP; 27411819Sjulian spxstat.spxs_accepts++; 27511819Sjulian cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 27611819Sjulian } 27711819Sjulian break; 27811819Sjulian /* 27911819Sjulian * This state means that we have heard a response 28011819Sjulian * to our acceptance of their connection 28111819Sjulian * It is probably logically unnecessary in this 28211819Sjulian * implementation. 28311819Sjulian */ 28411819Sjulian case TCPS_SYN_RECEIVED: { 28525652Sjhay if (si->si_did != cb->s_sid) { 28611819Sjulian spx_istat.wrncon++; 28711819Sjulian goto drop; 28811819Sjulian } 28911819Sjulian#endif 29011819Sjulian ipxp->ipxp_fport = si->si_sport; 29111819Sjulian cb->s_timer[SPXT_REXMT] = 0; 29211819Sjulian cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 29311819Sjulian soisconnected(so); 29411819Sjulian cb->s_state = TCPS_ESTABLISHED; 29511819Sjulian spxstat.spxs_accepts++; 29611819Sjulian } 29711819Sjulian break; 29811819Sjulian 29911819Sjulian /* 30011819Sjulian * This state means that we have gotten a response 30111819Sjulian * to our attempt to establish a connection. 30211819Sjulian * We fill in the data from the other side, 30311819Sjulian * telling us which port to respond to, instead of the well- 30411819Sjulian * known one we might have sent to in the first place. 30511819Sjulian * We also require that this is a response to our 30611819Sjulian * connection id. 30711819Sjulian */ 30811819Sjulian case TCPS_SYN_SENT: 30925652Sjhay if (si->si_did != cb->s_sid) { 31011819Sjulian spx_istat.notme++; 31111819Sjulian goto drop; 31211819Sjulian } 31311819Sjulian spxstat.spxs_connects++; 31411819Sjulian cb->s_did = si->si_sid; 31511819Sjulian cb->s_rack = si->si_ack; 31611819Sjulian cb->s_ralo = si->si_alo; 31711819Sjulian cb->s_dport = ipxp->ipxp_fport = si->si_sport; 31811819Sjulian cb->s_timer[SPXT_REXMT] = 0; 31911819Sjulian cb->s_flags |= SF_ACKNOW; 32011819Sjulian soisconnected(so); 32111819Sjulian cb->s_state = TCPS_ESTABLISHED; 32211819Sjulian /* Use roundtrip time of connection request for initial rtt */ 32311819Sjulian if (cb->s_rtt) { 32411819Sjulian cb->s_srtt = cb->s_rtt << 3; 32511819Sjulian cb->s_rttvar = cb->s_rtt << 1; 32611819Sjulian SPXT_RANGESET(cb->s_rxtcur, 32711819Sjulian ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1, 32811819Sjulian SPXTV_MIN, SPXTV_REXMTMAX); 32911819Sjulian cb->s_rtt = 0; 33011819Sjulian } 33111819Sjulian } 33297658Stanimura if (so->so_options & SO_DEBUG || traceallspxs) 33311819Sjulian spx_trace(SA_INPUT, (u_char)ostate, cb, &spx_savesi, 0); 33411819Sjulian 33525652Sjhay m->m_len -= sizeof(struct ipx); 33625652Sjhay m->m_pkthdr.len -= sizeof(struct ipx); 33725652Sjhay m->m_data += sizeof(struct ipx); 33811819Sjulian 33911819Sjulian if (spx_reass(cb, si)) { 34025652Sjhay m_freem(m); 34111819Sjulian } 34211819Sjulian if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT))) 343139579Srwatson spx_output(cb, NULL); 34411819Sjulian cb->s_flags &= ~(SF_WIN|SF_RXT); 345139932Srwatson IPX_UNLOCK(ipxp); 346139932Srwatson IPX_LIST_UNLOCK(); 34711819Sjulian return; 34811819Sjulian 34911819Sjuliandropwithreset: 350139932Srwatson IPX_UNLOCK(ipxp); 351130822Srwatson if (dropsocket) { 352130822Srwatson struct socket *head; 353130822Srwatson ACCEPT_LOCK(); 354130822Srwatson KASSERT((so->so_qstate & SQ_INCOMP) != 0, 355130822Srwatson ("spx_input: nascent socket not SQ_INCOMP on soabort()")); 356130822Srwatson head = so->so_head; 357130822Srwatson TAILQ_REMOVE(&head->so_incomp, so, so_list); 358130822Srwatson head->so_incqlen--; 359130822Srwatson so->so_qstate &= ~SQ_INCOMP; 360130822Srwatson so->so_head = NULL; 361130822Srwatson ACCEPT_UNLOCK(); 36225652Sjhay soabort(so); 363139931Srwatson cb = NULL; 364130822Srwatson } 365139932Srwatson IPX_LIST_UNLOCK(); 36611819Sjulian si->si_seq = ntohs(si->si_seq); 36711819Sjulian si->si_ack = ntohs(si->si_ack); 36811819Sjulian si->si_alo = ntohs(si->si_alo); 36925652Sjhay m_freem(dtom(si)); 370139931Srwatson if (cb == NULL || cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || 371139931Srwatson traceallspxs) 37211819Sjulian spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0); 37311819Sjulian return; 37411819Sjulian 37511819Sjuliandrop: 37611819Sjulianbad: 377139580Srwatson if (cb == NULL || cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || 37897658Stanimura traceallspxs) 37911819Sjulian spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0); 380139932Srwatson IPX_UNLOCK(ipxp); 381139932Srwatson IPX_LIST_UNLOCK(); 38211819Sjulian m_freem(m); 38311819Sjulian} 38411819Sjulian 38511819Sjulian/* 38611819Sjulian * This is structurally similar to the tcp reassembly routine 38711819Sjulian * but its function is somewhat different: It merely queues 38811819Sjulian * packets up, and suppresses duplicates. 38911819Sjulian */ 39025652Sjhaystatic int 391157067Srwatsonspx_reass(struct spxpcb *cb, struct spx *si) 39211819Sjulian{ 393157067Srwatson struct spx_q *q; 394157067Srwatson struct mbuf *m; 395157067Srwatson struct socket *so = cb->s_ipxpcb->ipxp_socket; 39611819Sjulian char packetp = cb->s_flags & SF_HI; 39711819Sjulian int incr; 39811819Sjulian char wakeup = 0; 39911819Sjulian 400139932Srwatson IPX_LOCK_ASSERT(cb->s_ipxpcb); 401139932Srwatson 40211819Sjulian if (si == SI(0)) 40311819Sjulian goto present; 40411819Sjulian /* 40511819Sjulian * Update our news from them. 40611819Sjulian */ 40711819Sjulian if (si->si_cc & SPX_SA) 40811819Sjulian cb->s_flags |= (spx_use_delack ? SF_DELACK : SF_ACKNOW); 40911819Sjulian if (SSEQ_GT(si->si_alo, cb->s_ralo)) 41011819Sjulian cb->s_flags |= SF_WIN; 41111819Sjulian if (SSEQ_LEQ(si->si_ack, cb->s_rack)) { 41211819Sjulian if ((si->si_cc & SPX_SP) && cb->s_rack != (cb->s_smax + 1)) { 41311819Sjulian spxstat.spxs_rcvdupack++; 41411819Sjulian /* 41511819Sjulian * If this is a completely duplicate ack 41611819Sjulian * and other conditions hold, we assume 41711819Sjulian * a packet has been dropped and retransmit 41811819Sjulian * it exactly as in tcp_input(). 41911819Sjulian */ 42011819Sjulian if (si->si_ack != cb->s_rack || 42111819Sjulian si->si_alo != cb->s_ralo) 42211819Sjulian cb->s_dupacks = 0; 42311819Sjulian else if (++cb->s_dupacks == spxrexmtthresh) { 42411819Sjulian u_short onxt = cb->s_snxt; 42511819Sjulian int cwnd = cb->s_cwnd; 42611819Sjulian 42711819Sjulian cb->s_snxt = si->si_ack; 42811819Sjulian cb->s_cwnd = CUNIT; 42911819Sjulian cb->s_force = 1 + SPXT_REXMT; 430139579Srwatson spx_output(cb, NULL); 43111819Sjulian cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 43211819Sjulian cb->s_rtt = 0; 43311819Sjulian if (cwnd >= 4 * CUNIT) 43411819Sjulian cb->s_cwnd = cwnd / 2; 43511819Sjulian if (SSEQ_GT(onxt, cb->s_snxt)) 43611819Sjulian cb->s_snxt = onxt; 43711819Sjulian return (1); 43811819Sjulian } 43911819Sjulian } else 44011819Sjulian cb->s_dupacks = 0; 44111819Sjulian goto update_window; 44211819Sjulian } 44311819Sjulian cb->s_dupacks = 0; 44411819Sjulian /* 44511819Sjulian * If our correspondent acknowledges data we haven't sent 44611819Sjulian * TCP would drop the packet after acking. We'll be a little 44711819Sjulian * more permissive 44811819Sjulian */ 44911819Sjulian if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) { 45011819Sjulian spxstat.spxs_rcvacktoomuch++; 45111819Sjulian si->si_ack = cb->s_smax + 1; 45211819Sjulian } 45311819Sjulian spxstat.spxs_rcvackpack++; 45411819Sjulian /* 45511819Sjulian * If transmit timer is running and timed sequence 45611819Sjulian * number was acked, update smoothed round trip time. 45711819Sjulian * See discussion of algorithm in tcp_input.c 45811819Sjulian */ 45911819Sjulian if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) { 46011819Sjulian spxstat.spxs_rttupdated++; 46111819Sjulian if (cb->s_srtt != 0) { 462157067Srwatson short delta; 46311819Sjulian delta = cb->s_rtt - (cb->s_srtt >> 3); 46411819Sjulian if ((cb->s_srtt += delta) <= 0) 46511819Sjulian cb->s_srtt = 1; 46611819Sjulian if (delta < 0) 46711819Sjulian delta = -delta; 46811819Sjulian delta -= (cb->s_rttvar >> 2); 46911819Sjulian if ((cb->s_rttvar += delta) <= 0) 47011819Sjulian cb->s_rttvar = 1; 47111819Sjulian } else { 47211819Sjulian /* 47311819Sjulian * No rtt measurement yet 47411819Sjulian */ 47511819Sjulian cb->s_srtt = cb->s_rtt << 3; 47611819Sjulian cb->s_rttvar = cb->s_rtt << 1; 47711819Sjulian } 47811819Sjulian cb->s_rtt = 0; 47911819Sjulian cb->s_rxtshift = 0; 48011819Sjulian SPXT_RANGESET(cb->s_rxtcur, 48111819Sjulian ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1, 48211819Sjulian SPXTV_MIN, SPXTV_REXMTMAX); 48311819Sjulian } 48411819Sjulian /* 48511819Sjulian * If all outstanding data is acked, stop retransmit 48611819Sjulian * timer and remember to restart (more output or persist). 48711819Sjulian * If there is more data to be acked, restart retransmit 48811819Sjulian * timer, using current (possibly backed-off) value; 48911819Sjulian */ 49011819Sjulian if (si->si_ack == cb->s_smax + 1) { 49111819Sjulian cb->s_timer[SPXT_REXMT] = 0; 49211819Sjulian cb->s_flags |= SF_RXT; 49311819Sjulian } else if (cb->s_timer[SPXT_PERSIST] == 0) 49411819Sjulian cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 49511819Sjulian /* 49611819Sjulian * When new data is acked, open the congestion window. 49711819Sjulian * If the window gives us less than ssthresh packets 49811819Sjulian * in flight, open exponentially (maxseg at a time). 49911819Sjulian * Otherwise open linearly (maxseg^2 / cwnd at a time). 50011819Sjulian */ 50111819Sjulian incr = CUNIT; 50211819Sjulian if (cb->s_cwnd > cb->s_ssthresh) 50311819Sjulian incr = max(incr * incr / cb->s_cwnd, 1); 50411819Sjulian cb->s_cwnd = min(cb->s_cwnd + incr, cb->s_cwmx); 50511819Sjulian /* 50611819Sjulian * Trim Acked data from output queue. 50711819Sjulian */ 508139589Srwatson SOCKBUF_LOCK(&so->so_snd); 50911819Sjulian while ((m = so->so_snd.sb_mb) != NULL) { 51011819Sjulian if (SSEQ_LT((mtod(m, struct spx *))->si_seq, si->si_ack)) 511139589Srwatson sbdroprecord_locked(&so->so_snd); 51211819Sjulian else 51311819Sjulian break; 51411819Sjulian } 515139589Srwatson sowwakeup_locked(so); 51611819Sjulian cb->s_rack = si->si_ack; 51711819Sjulianupdate_window: 51811819Sjulian if (SSEQ_LT(cb->s_snxt, cb->s_rack)) 51911819Sjulian cb->s_snxt = cb->s_rack; 52043311Sdillon if (SSEQ_LT(cb->s_swl1, si->si_seq) || ((cb->s_swl1 == si->si_seq && 52143311Sdillon (SSEQ_LT(cb->s_swl2, si->si_ack))) || 52243305Sdillon (cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo)))) { 52311819Sjulian /* keep track of pure window updates */ 52411819Sjulian if ((si->si_cc & SPX_SP) && cb->s_swl2 == si->si_ack 52511819Sjulian && SSEQ_LT(cb->s_ralo, si->si_alo)) { 52611819Sjulian spxstat.spxs_rcvwinupd++; 52711819Sjulian spxstat.spxs_rcvdupack--; 52811819Sjulian } 52911819Sjulian cb->s_ralo = si->si_alo; 53011819Sjulian cb->s_swl1 = si->si_seq; 53111819Sjulian cb->s_swl2 = si->si_ack; 53211819Sjulian cb->s_swnd = (1 + si->si_alo - si->si_ack); 53311819Sjulian if (cb->s_swnd > cb->s_smxw) 53411819Sjulian cb->s_smxw = cb->s_swnd; 53511819Sjulian cb->s_flags |= SF_WIN; 53611819Sjulian } 53711819Sjulian /* 53811819Sjulian * If this packet number is higher than that which 53911819Sjulian * we have allocated refuse it, unless urgent 54011819Sjulian */ 54111819Sjulian if (SSEQ_GT(si->si_seq, cb->s_alo)) { 54211819Sjulian if (si->si_cc & SPX_SP) { 54311819Sjulian spxstat.spxs_rcvwinprobe++; 54411819Sjulian return (1); 54511819Sjulian } else 54611819Sjulian spxstat.spxs_rcvpackafterwin++; 54711819Sjulian if (si->si_cc & SPX_OB) { 54811819Sjulian if (SSEQ_GT(si->si_seq, cb->s_alo + 60)) { 54925652Sjhay m_freem(dtom(si)); 55011819Sjulian return (0); 55111819Sjulian } /* else queue this packet; */ 55211819Sjulian } else { 553139579Srwatson#ifdef BROKEN 554139579Srwatson /* 555139579Srwatson * XXXRW: This is broken on at least one count: 556139579Srwatson * spx_close() will free the ipxp and related parts, 557139579Srwatson * which are then touched by spx_input() after the 558139579Srwatson * return from spx_reass(). 559139579Srwatson */ 560157067Srwatson /*struct socket *so = cb->s_ipxpcb->ipxp_socket; 56111819Sjulian if (so->so_state && SS_NOFDREF) { 56225652Sjhay spx_close(cb); 56397658Stanimura } else 56497658Stanimura would crash system*/ 565139579Srwatson#endif 56611819Sjulian spx_istat.notyet++; 56725652Sjhay m_freem(dtom(si)); 56811819Sjulian return (0); 56911819Sjulian } 57011819Sjulian } 57111819Sjulian /* 57211819Sjulian * If this is a system packet, we don't need to 57311819Sjulian * queue it up, and won't update acknowledge # 57411819Sjulian */ 57511819Sjulian if (si->si_cc & SPX_SP) { 57611819Sjulian return (1); 57711819Sjulian } 57811819Sjulian /* 57911819Sjulian * We have already seen this packet, so drop. 58011819Sjulian */ 58111819Sjulian if (SSEQ_LT(si->si_seq, cb->s_ack)) { 58211819Sjulian spx_istat.bdreas++; 58311819Sjulian spxstat.spxs_rcvduppack++; 58411819Sjulian if (si->si_seq == cb->s_ack - 1) 58511819Sjulian spx_istat.lstdup++; 58611819Sjulian return (1); 58711819Sjulian } 58811819Sjulian /* 58911819Sjulian * Loop through all packets queued up to insert in 59011819Sjulian * appropriate sequence. 59111819Sjulian */ 59225652Sjhay for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) { 59311819Sjulian if (si->si_seq == SI(q)->si_seq) { 59411819Sjulian spxstat.spxs_rcvduppack++; 59511819Sjulian return (1); 59611819Sjulian } 59711819Sjulian if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) { 59811819Sjulian spxstat.spxs_rcvoopack++; 59911819Sjulian break; 60011819Sjulian } 60111819Sjulian } 60211819Sjulian insque(si, q->si_prev); 60311819Sjulian /* 60411819Sjulian * If this packet is urgent, inform process 60511819Sjulian */ 60611819Sjulian if (si->si_cc & SPX_OB) { 60711819Sjulian cb->s_iobc = ((char *)si)[1 + sizeof(*si)]; 60811819Sjulian sohasoutofband(so); 60911819Sjulian cb->s_oobflags |= SF_IOOB; 61011819Sjulian } 61111819Sjulianpresent: 61211819Sjulian#define SPINC sizeof(struct spxhdr) 613139590Srwatson SOCKBUF_LOCK(&so->so_rcv); 61411819Sjulian /* 61511819Sjulian * Loop through all packets queued up to update acknowledge 61611819Sjulian * number, and present all acknowledged data to user; 61711819Sjulian * If in packet interface mode, show packet headers. 61811819Sjulian */ 61925652Sjhay for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) { 62011819Sjulian if (SI(q)->si_seq == cb->s_ack) { 62111819Sjulian cb->s_ack++; 62211819Sjulian m = dtom(q); 62311819Sjulian if (SI(q)->si_cc & SPX_OB) { 62411819Sjulian cb->s_oobflags &= ~SF_IOOB; 62511819Sjulian if (so->so_rcv.sb_cc) 62611819Sjulian so->so_oobmark = so->so_rcv.sb_cc; 627131031Srwatson else 628130480Srwatson so->so_rcv.sb_state |= SBS_RCVATMARK; 62911819Sjulian } 63011819Sjulian q = q->si_prev; 63111819Sjulian remque(q->si_next); 63211819Sjulian wakeup = 1; 63311819Sjulian spxstat.spxs_rcvpack++; 63411819Sjulian#ifdef SF_NEWCALL 63511819Sjulian if (cb->s_flags2 & SF_NEWCALL) { 63611819Sjulian struct spxhdr *sp = mtod(m, struct spxhdr *); 63711819Sjulian u_char dt = sp->spx_dt; 63811819Sjulian spx_newchecks[4]++; 63911819Sjulian if (dt != cb->s_rhdr.spx_dt) { 64011819Sjulian struct mbuf *mm = 641111119Simp m_getclr(M_DONTWAIT, MT_CONTROL); 64211819Sjulian spx_newchecks[0]++; 64311819Sjulian if (mm != NULL) { 64411819Sjulian u_short *s = 64511819Sjulian mtod(mm, u_short *); 64611819Sjulian cb->s_rhdr.spx_dt = dt; 64711819Sjulian mm->m_len = 5; /*XXX*/ 64811819Sjulian s[0] = 5; 64911819Sjulian s[1] = 1; 65011819Sjulian *(u_char *)(&s[2]) = dt; 651139590Srwatson sbappend_locked(&so->so_rcv, mm); 65211819Sjulian } 65311819Sjulian } 65411819Sjulian if (sp->spx_cc & SPX_OB) { 65511819Sjulian MCHTYPE(m, MT_OOBDATA); 65611819Sjulian spx_newchecks[1]++; 65711819Sjulian so->so_oobmark = 0; 658130480Srwatson so->so_rcv.sb_state &= ~SBS_RCVATMARK; 65911819Sjulian } 66011819Sjulian if (packetp == 0) { 66111819Sjulian m->m_data += SPINC; 66211819Sjulian m->m_len -= SPINC; 66311819Sjulian m->m_pkthdr.len -= SPINC; 66411819Sjulian } 66511819Sjulian if ((sp->spx_cc & SPX_EM) || packetp) { 666139590Srwatson sbappendrecord_locked(&so->so_rcv, m); 66711819Sjulian spx_newchecks[9]++; 66811819Sjulian } else 669139590Srwatson sbappend_locked(&so->so_rcv, m); 67011819Sjulian } else 67111819Sjulian#endif 67211819Sjulian if (packetp) { 673139590Srwatson sbappendrecord_locked(&so->so_rcv, m); 67411819Sjulian } else { 67511819Sjulian cb->s_rhdr = *mtod(m, struct spxhdr *); 67611819Sjulian m->m_data += SPINC; 67711819Sjulian m->m_len -= SPINC; 67811819Sjulian m->m_pkthdr.len -= SPINC; 679139590Srwatson sbappend_locked(&so->so_rcv, m); 68011819Sjulian } 68111819Sjulian } else 68211819Sjulian break; 68311819Sjulian } 68497658Stanimura if (wakeup) 685139590Srwatson sorwakeup_locked(so); 686139590Srwatson else 687139590Srwatson SOCKBUF_UNLOCK(&so->so_rcv); 68811819Sjulian return (0); 68911819Sjulian} 69011819Sjulian 69111819Sjulianvoid 692157067Srwatsonspx_ctlinput(int cmd, struct sockaddr *arg_as_sa, void *dummy) 69311819Sjulian{ 69411819Sjulian 695157050Srwatson /* Currently, nothing. */ 69611819Sjulian} 69711819Sjulian 69825652Sjhaystatic int 699157067Srwatsonspx_output(struct spxpcb *cb, struct mbuf *m0) 70011819Sjulian{ 70111819Sjulian struct socket *so = cb->s_ipxpcb->ipxp_socket; 702157067Srwatson struct mbuf *m; 703157067Srwatson struct spx *si = NULL; 704157067Srwatson struct sockbuf *sb = &so->so_snd; 70511819Sjulian int len = 0, win, rcv_win; 70611819Sjulian short span, off, recordp = 0; 70711819Sjulian u_short alo; 70811819Sjulian int error = 0, sendalot; 70911819Sjulian#ifdef notdef 71011819Sjulian int idle; 71111819Sjulian#endif 71211819Sjulian struct mbuf *mprev; 71311819Sjulian 714139932Srwatson IPX_LOCK_ASSERT(cb->s_ipxpcb); 715139932Srwatson 71625652Sjhay if (m0 != NULL) { 71711819Sjulian int mtu = cb->s_mtu; 71811819Sjulian int datalen; 71911819Sjulian /* 72011819Sjulian * Make sure that packet isn't too big. 72111819Sjulian */ 72225652Sjhay for (m = m0; m != NULL; m = m->m_next) { 72311819Sjulian mprev = m; 72411819Sjulian len += m->m_len; 72511819Sjulian if (m->m_flags & M_EOR) 72611819Sjulian recordp = 1; 72711819Sjulian } 72811819Sjulian datalen = (cb->s_flags & SF_HO) ? 72925652Sjhay len - sizeof(struct spxhdr) : len; 73011819Sjulian if (datalen > mtu) { 73111819Sjulian if (cb->s_flags & SF_PI) { 73211819Sjulian m_freem(m0); 73311819Sjulian return (EMSGSIZE); 73411819Sjulian } else { 73511819Sjulian int oldEM = cb->s_cc & SPX_EM; 73611819Sjulian 73711819Sjulian cb->s_cc &= ~SPX_EM; 73811819Sjulian while (len > mtu) { 73911819Sjulian /* 74011819Sjulian * Here we are only being called 74111819Sjulian * from usrreq(), so it is OK to 74211819Sjulian * block. 74311819Sjulian */ 744111119Simp m = m_copym(m0, 0, mtu, M_TRYWAIT); 74511819Sjulian if (cb->s_flags & SF_NEWCALL) { 74611819Sjulian struct mbuf *mm = m; 74711819Sjulian spx_newchecks[7]++; 74825652Sjhay while (mm != NULL) { 74911819Sjulian mm->m_flags &= ~M_EOR; 75011819Sjulian mm = mm->m_next; 75111819Sjulian } 75211819Sjulian } 75311819Sjulian error = spx_output(cb, m); 75411819Sjulian if (error) { 75511819Sjulian cb->s_cc |= oldEM; 75611819Sjulian m_freem(m0); 75725652Sjhay return (error); 75811819Sjulian } 75911819Sjulian m_adj(m0, mtu); 76011819Sjulian len -= mtu; 76111819Sjulian } 76211819Sjulian cb->s_cc |= oldEM; 76311819Sjulian } 76411819Sjulian } 76511819Sjulian /* 76611819Sjulian * Force length even, by adding a "garbage byte" if 76711819Sjulian * necessary. 76811819Sjulian */ 76911819Sjulian if (len & 1) { 77011819Sjulian m = mprev; 77111819Sjulian if (M_TRAILINGSPACE(m) >= 1) 77211819Sjulian m->m_len++; 77311819Sjulian else { 774111119Simp struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA); 77511819Sjulian 77625652Sjhay if (m1 == NULL) { 77711819Sjulian m_freem(m0); 77811819Sjulian return (ENOBUFS); 77911819Sjulian } 78011819Sjulian m1->m_len = 1; 78111819Sjulian *(mtod(m1, u_char *)) = 0; 78211819Sjulian m->m_next = m1; 78311819Sjulian } 78411819Sjulian } 785151967Sandre m = m_gethdr(M_DONTWAIT, MT_DATA); 78625652Sjhay if (m == NULL) { 78711819Sjulian m_freem(m0); 78811819Sjulian return (ENOBUFS); 78911819Sjulian } 79011819Sjulian /* 79111819Sjulian * Fill in mbuf with extended SP header 79211819Sjulian * and addresses and length put into network format. 79311819Sjulian */ 79425652Sjhay MH_ALIGN(m, sizeof(struct spx)); 79525652Sjhay m->m_len = sizeof(struct spx); 79611819Sjulian m->m_next = m0; 79711819Sjulian si = mtod(m, struct spx *); 79811819Sjulian si->si_i = *cb->s_ipx; 79911819Sjulian si->si_s = cb->s_shdr; 80011819Sjulian if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) { 801157067Srwatson struct spxhdr *sh; 80225652Sjhay if (m0->m_len < sizeof(*sh)) { 80311819Sjulian if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) { 80425652Sjhay m_free(m); 80511819Sjulian m_freem(m0); 80611819Sjulian return (EINVAL); 80711819Sjulian } 80811819Sjulian m->m_next = m0; 80911819Sjulian } 81011819Sjulian sh = mtod(m0, struct spxhdr *); 81111819Sjulian si->si_dt = sh->spx_dt; 81211819Sjulian si->si_cc |= sh->spx_cc & SPX_EM; 81325652Sjhay m0->m_len -= sizeof(*sh); 81425652Sjhay m0->m_data += sizeof(*sh); 81525652Sjhay len -= sizeof(*sh); 81611819Sjulian } 81711819Sjulian len += sizeof(*si); 81811819Sjulian if ((cb->s_flags2 & SF_NEWCALL) && recordp) { 81925652Sjhay si->si_cc |= SPX_EM; 82011819Sjulian spx_newchecks[8]++; 82111819Sjulian } 82211819Sjulian if (cb->s_oobflags & SF_SOOB) { 82311819Sjulian /* 82411819Sjulian * Per jqj@cornell: 82511819Sjulian * make sure OB packets convey exactly 1 byte. 82611819Sjulian * If the packet is 1 byte or larger, we 82711819Sjulian * have already guaranted there to be at least 82811819Sjulian * one garbage byte for the checksum, and 82911819Sjulian * extra bytes shouldn't hurt! 83011819Sjulian */ 83111819Sjulian if (len > sizeof(*si)) { 83211819Sjulian si->si_cc |= SPX_OB; 83311819Sjulian len = (1 + sizeof(*si)); 83411819Sjulian } 83511819Sjulian } 83611819Sjulian si->si_len = htons((u_short)len); 83711819Sjulian m->m_pkthdr.len = ((len - 1) | 1) + 1; 83811819Sjulian /* 83911819Sjulian * queue stuff up for output 84011819Sjulian */ 84111819Sjulian sbappendrecord(sb, m); 84211819Sjulian cb->s_seq++; 84311819Sjulian } 84411819Sjulian#ifdef notdef 84511819Sjulian idle = (cb->s_smax == (cb->s_rack - 1)); 84611819Sjulian#endif 84711819Sjulianagain: 84811819Sjulian sendalot = 0; 84911819Sjulian off = cb->s_snxt - cb->s_rack; 85025652Sjhay win = min(cb->s_swnd, (cb->s_cwnd / CUNIT)); 85111819Sjulian 85211819Sjulian /* 85311819Sjulian * If in persist timeout with window of 0, send a probe. 85411819Sjulian * Otherwise, if window is small but nonzero 85511819Sjulian * and timer expired, send what we can and go into 85611819Sjulian * transmit state. 85711819Sjulian */ 85811819Sjulian if (cb->s_force == 1 + SPXT_PERSIST) { 85911819Sjulian if (win != 0) { 86011819Sjulian cb->s_timer[SPXT_PERSIST] = 0; 86111819Sjulian cb->s_rxtshift = 0; 86211819Sjulian } 86311819Sjulian } 86411819Sjulian span = cb->s_seq - cb->s_rack; 86511819Sjulian len = min(span, win) - off; 86611819Sjulian 86711819Sjulian if (len < 0) { 86811819Sjulian /* 86911819Sjulian * Window shrank after we went into it. 87011819Sjulian * If window shrank to 0, cancel pending 87111819Sjulian * restransmission and pull s_snxt back 87211819Sjulian * to (closed) window. We will enter persist 87311819Sjulian * state below. If the widndow didn't close completely, 87411819Sjulian * just wait for an ACK. 87511819Sjulian */ 87611819Sjulian len = 0; 87711819Sjulian if (win == 0) { 87811819Sjulian cb->s_timer[SPXT_REXMT] = 0; 87911819Sjulian cb->s_snxt = cb->s_rack; 88011819Sjulian } 88111819Sjulian } 88211819Sjulian if (len > 1) 88311819Sjulian sendalot = 1; 88411819Sjulian rcv_win = sbspace(&so->so_rcv); 88511819Sjulian 88611819Sjulian /* 88711819Sjulian * Send if we owe peer an ACK. 88811819Sjulian */ 88911819Sjulian if (cb->s_oobflags & SF_SOOB) { 89011819Sjulian /* 89111819Sjulian * must transmit this out of band packet 89211819Sjulian */ 89311819Sjulian cb->s_oobflags &= ~ SF_SOOB; 89411819Sjulian sendalot = 1; 89511819Sjulian spxstat.spxs_sndurg++; 89611819Sjulian goto found; 89711819Sjulian } 89811819Sjulian if (cb->s_flags & SF_ACKNOW) 89911819Sjulian goto send; 90011819Sjulian if (cb->s_state < TCPS_ESTABLISHED) 90111819Sjulian goto send; 90211819Sjulian /* 90311819Sjulian * Silly window can't happen in spx. 90411819Sjulian * Code from tcp deleted. 90511819Sjulian */ 90611819Sjulian if (len) 90711819Sjulian goto send; 90811819Sjulian /* 90911819Sjulian * Compare available window to amount of window 91011819Sjulian * known to peer (as advertised window less 91111819Sjulian * next expected input.) If the difference is at least two 91211819Sjulian * packets or at least 35% of the mximum possible window, 91311819Sjulian * then want to send a window update to peer. 91411819Sjulian */ 91511819Sjulian if (rcv_win > 0) { 91611819Sjulian u_short delta = 1 + cb->s_alo - cb->s_ack; 91711819Sjulian int adv = rcv_win - (delta * cb->s_mtu); 918139584Srwatson 91911819Sjulian if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) || 92011819Sjulian (100 * adv / so->so_rcv.sb_hiwat >= 35)) { 92111819Sjulian spxstat.spxs_sndwinup++; 92211819Sjulian cb->s_flags |= SF_ACKNOW; 92311819Sjulian goto send; 92411819Sjulian } 92511819Sjulian 92611819Sjulian } 92711819Sjulian /* 92811819Sjulian * Many comments from tcp_output.c are appropriate here 92911819Sjulian * including . . . 93011819Sjulian * If send window is too small, there is data to transmit, and no 93111819Sjulian * retransmit or persist is pending, then go to persist state. 93211819Sjulian * If nothing happens soon, send when timer expires: 93311819Sjulian * if window is nonzero, transmit what we can, 93411819Sjulian * otherwise send a probe. 93511819Sjulian */ 93611819Sjulian if (so->so_snd.sb_cc && cb->s_timer[SPXT_REXMT] == 0 && 93711819Sjulian cb->s_timer[SPXT_PERSIST] == 0) { 93811819Sjulian cb->s_rxtshift = 0; 93911819Sjulian spx_setpersist(cb); 94011819Sjulian } 94111819Sjulian /* 94211819Sjulian * No reason to send a packet, just return. 94311819Sjulian */ 94411819Sjulian cb->s_outx = 1; 94511819Sjulian return (0); 94611819Sjulian 94711819Sjuliansend: 94811819Sjulian /* 94911819Sjulian * Find requested packet. 95011819Sjulian */ 95111819Sjulian si = 0; 95211819Sjulian if (len > 0) { 95311819Sjulian cb->s_want = cb->s_snxt; 95425652Sjhay for (m = sb->sb_mb; m != NULL; m = m->m_act) { 95511819Sjulian si = mtod(m, struct spx *); 95611819Sjulian if (SSEQ_LEQ(cb->s_snxt, si->si_seq)) 95711819Sjulian break; 95811819Sjulian } 95911819Sjulian found: 96025652Sjhay if (si != NULL) { 96111819Sjulian if (si->si_seq == cb->s_snxt) 96211819Sjulian cb->s_snxt++; 96311819Sjulian else 96411819Sjulian spxstat.spxs_sndvoid++, si = 0; 96511819Sjulian } 96611819Sjulian } 96711819Sjulian /* 96811819Sjulian * update window 96911819Sjulian */ 97011819Sjulian if (rcv_win < 0) 97111819Sjulian rcv_win = 0; 97211819Sjulian alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu)); 973139584Srwatson if (SSEQ_LT(alo, cb->s_alo)) 97411819Sjulian alo = cb->s_alo; 97511819Sjulian 97625652Sjhay if (si != NULL) { 97711819Sjulian /* 97811819Sjulian * must make a copy of this packet for 97911819Sjulian * ipx_output to monkey with 98011819Sjulian */ 98111819Sjulian m = m_copy(dtom(si), 0, (int)M_COPYALL); 98211819Sjulian if (m == NULL) { 98311819Sjulian return (ENOBUFS); 98411819Sjulian } 98511819Sjulian si = mtod(m, struct spx *); 98611819Sjulian if (SSEQ_LT(si->si_seq, cb->s_smax)) 98711819Sjulian spxstat.spxs_sndrexmitpack++; 98811819Sjulian else 98911819Sjulian spxstat.spxs_sndpack++; 99011819Sjulian } else if (cb->s_force || cb->s_flags & SF_ACKNOW) { 99111819Sjulian /* 99211819Sjulian * Must send an acknowledgement or a probe 99311819Sjulian */ 99411819Sjulian if (cb->s_force) 99511819Sjulian spxstat.spxs_sndprobe++; 99611819Sjulian if (cb->s_flags & SF_ACKNOW) 99711819Sjulian spxstat.spxs_sndacks++; 998151967Sandre m = m_gethdr(M_DONTWAIT, MT_DATA); 99925652Sjhay if (m == NULL) 100011819Sjulian return (ENOBUFS); 100111819Sjulian /* 100211819Sjulian * Fill in mbuf with extended SP header 100311819Sjulian * and addresses and length put into network format. 100411819Sjulian */ 100525652Sjhay MH_ALIGN(m, sizeof(struct spx)); 100625652Sjhay m->m_len = sizeof(*si); 100725652Sjhay m->m_pkthdr.len = sizeof(*si); 100811819Sjulian si = mtod(m, struct spx *); 100911819Sjulian si->si_i = *cb->s_ipx; 101011819Sjulian si->si_s = cb->s_shdr; 101111819Sjulian si->si_seq = cb->s_smax + 1; 101225652Sjhay si->si_len = htons(sizeof(*si)); 101311819Sjulian si->si_cc |= SPX_SP; 101411819Sjulian } else { 101511819Sjulian cb->s_outx = 3; 101697658Stanimura if (so->so_options & SO_DEBUG || traceallspxs) 101711819Sjulian spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0); 101811819Sjulian return (0); 101911819Sjulian } 102011819Sjulian /* 102111819Sjulian * Stuff checksum and output datagram. 102211819Sjulian */ 102311819Sjulian if ((si->si_cc & SPX_SP) == 0) { 102411819Sjulian if (cb->s_force != (1 + SPXT_PERSIST) || 102511819Sjulian cb->s_timer[SPXT_PERSIST] == 0) { 102611819Sjulian /* 1027139584Srwatson * If this is a new packet and we are not currently 102811819Sjulian * timing anything, time this one. 102911819Sjulian */ 103011819Sjulian if (SSEQ_LT(cb->s_smax, si->si_seq)) { 103111819Sjulian cb->s_smax = si->si_seq; 103211819Sjulian if (cb->s_rtt == 0) { 103311819Sjulian spxstat.spxs_segstimed++; 103411819Sjulian cb->s_rtseq = si->si_seq; 103511819Sjulian cb->s_rtt = 1; 103611819Sjulian } 103711819Sjulian } 103811819Sjulian /* 103911819Sjulian * Set rexmt timer if not currently set, 104011819Sjulian * Initial value for retransmit timer is smoothed 104111819Sjulian * round-trip time + 2 * round-trip time variance. 104211819Sjulian * Initialize shift counter which is used for backoff 104311819Sjulian * of retransmit time. 104411819Sjulian */ 104511819Sjulian if (cb->s_timer[SPXT_REXMT] == 0 && 104611819Sjulian cb->s_snxt != cb->s_rack) { 104711819Sjulian cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 104811819Sjulian if (cb->s_timer[SPXT_PERSIST]) { 104911819Sjulian cb->s_timer[SPXT_PERSIST] = 0; 105011819Sjulian cb->s_rxtshift = 0; 105111819Sjulian } 105211819Sjulian } 105311819Sjulian } else if (SSEQ_LT(cb->s_smax, si->si_seq)) { 105411819Sjulian cb->s_smax = si->si_seq; 105511819Sjulian } 105611819Sjulian } else if (cb->s_state < TCPS_ESTABLISHED) { 105711819Sjulian if (cb->s_rtt == 0) 105811819Sjulian cb->s_rtt = 1; /* Time initial handshake */ 105911819Sjulian if (cb->s_timer[SPXT_REXMT] == 0) 106011819Sjulian cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 106111819Sjulian } 106211819Sjulian { 106311819Sjulian /* 106411819Sjulian * Do not request acks when we ack their data packets or 106511819Sjulian * when we do a gratuitous window update. 106611819Sjulian */ 106711819Sjulian if (((si->si_cc & SPX_SP) == 0) || cb->s_force) 106811819Sjulian si->si_cc |= SPX_SA; 106911819Sjulian si->si_seq = htons(si->si_seq); 107011819Sjulian si->si_alo = htons(alo); 107111819Sjulian si->si_ack = htons(cb->s_ack); 107211819Sjulian 107311819Sjulian if (ipxcksum) { 107450519Sjhay si->si_sum = ipx_cksum(m, ntohs(si->si_len)); 107511819Sjulian } else 107611819Sjulian si->si_sum = 0xffff; 107711819Sjulian 107811819Sjulian cb->s_outx = 4; 107997658Stanimura if (so->so_options & SO_DEBUG || traceallspxs) 108011819Sjulian spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0); 108111819Sjulian 108297658Stanimura if (so->so_options & SO_DONTROUTE) 1083139579Srwatson error = ipx_outputfl(m, NULL, IPX_ROUTETOIF); 108497658Stanimura else 108511819Sjulian error = ipx_outputfl(m, &cb->s_ipxpcb->ipxp_route, 0); 108611819Sjulian } 108711819Sjulian if (error) { 108811819Sjulian return (error); 108911819Sjulian } 109011819Sjulian spxstat.spxs_sndtotal++; 109111819Sjulian /* 109211819Sjulian * Data sent (as far as we can tell). 109311819Sjulian * If this advertises a larger window than any other segment, 109411819Sjulian * then remember the size of the advertized window. 109511819Sjulian * Any pending ACK has now been sent. 109611819Sjulian */ 109711819Sjulian cb->s_force = 0; 109811819Sjulian cb->s_flags &= ~(SF_ACKNOW|SF_DELACK); 109911819Sjulian if (SSEQ_GT(alo, cb->s_alo)) 110011819Sjulian cb->s_alo = alo; 110111819Sjulian if (sendalot) 110211819Sjulian goto again; 110311819Sjulian cb->s_outx = 5; 110411819Sjulian return (0); 110511819Sjulian} 110611819Sjulian 110733181Seivindstatic int spx_do_persist_panics = 0; 110811819Sjulian 110925652Sjhaystatic void 1110157067Srwatsonspx_setpersist(struct spxpcb *cb) 111111819Sjulian{ 1112157067Srwatson int t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1; 111311819Sjulian 1114139932Srwatson IPX_LOCK_ASSERT(cb->s_ipxpcb); 1115139932Srwatson 111611819Sjulian if (cb->s_timer[SPXT_REXMT] && spx_do_persist_panics) 111711819Sjulian panic("spx_output REXMT"); 111811819Sjulian /* 111911819Sjulian * Start/restart persistance timer. 112011819Sjulian */ 112111819Sjulian SPXT_RANGESET(cb->s_timer[SPXT_PERSIST], 112211819Sjulian t*spx_backoff[cb->s_rxtshift], 112311819Sjulian SPXTV_PERSMIN, SPXTV_PERSMAX); 112411819Sjulian if (cb->s_rxtshift < SPX_MAXRXTSHIFT) 112511819Sjulian cb->s_rxtshift++; 112611819Sjulian} 112725652Sjhay 112811819Sjulianint 1129157067Srwatsonspx_ctloutput(struct socket *so, struct sockopt *sopt) 113011819Sjulian{ 113111819Sjulian struct ipxpcb *ipxp = sotoipxpcb(so); 1132157067Srwatson struct spxpcb *cb; 113338482Swollman int mask, error; 113438482Swollman short soptval; 113538482Swollman u_short usoptval; 113638482Swollman int optval; 113711819Sjulian 113838482Swollman error = 0; 113938482Swollman 114038482Swollman if (sopt->sopt_level != IPXPROTO_SPX) { 114111819Sjulian /* This will have to be changed when we do more general 114211819Sjulian stacking of protocols */ 114338482Swollman return (ipx_ctloutput(so, sopt)); 114411819Sjulian } 114538482Swollman if (ipxp == NULL) 114638482Swollman return (EINVAL); 114738482Swollman else 114811819Sjulian cb = ipxtospxpcb(ipxp); 114911819Sjulian 115038482Swollman switch (sopt->sopt_dir) { 115138482Swollman case SOPT_GET: 115238482Swollman switch (sopt->sopt_name) { 115311819Sjulian case SO_HEADERS_ON_INPUT: 115411819Sjulian mask = SF_HI; 115511819Sjulian goto get_flags; 115611819Sjulian 115711819Sjulian case SO_HEADERS_ON_OUTPUT: 115811819Sjulian mask = SF_HO; 115911819Sjulian get_flags: 1160139932Srwatson /* Unlocked read. */ 116138482Swollman soptval = cb->s_flags & mask; 116238482Swollman error = sooptcopyout(sopt, &soptval, sizeof soptval); 116311819Sjulian break; 116411819Sjulian 116511819Sjulian case SO_MTU: 1166139932Srwatson /* Unlocked read. */ 116738482Swollman usoptval = cb->s_mtu; 116838482Swollman error = sooptcopyout(sopt, &usoptval, sizeof usoptval); 116911819Sjulian break; 117011819Sjulian 117111819Sjulian case SO_LAST_HEADER: 1172139932Srwatson /* Unlocked read. */ 1173139584Srwatson error = sooptcopyout(sopt, &cb->s_rhdr, 117438482Swollman sizeof cb->s_rhdr); 117511819Sjulian break; 117611819Sjulian 117711819Sjulian case SO_DEFAULT_HEADERS: 1178139932Srwatson /* Unlocked read. */ 1179139584Srwatson error = sooptcopyout(sopt, &cb->s_shdr, 118038482Swollman sizeof cb->s_shdr); 118111819Sjulian break; 118211819Sjulian 118311819Sjulian default: 118438482Swollman error = ENOPROTOOPT; 118511819Sjulian } 118611819Sjulian break; 118711819Sjulian 118838482Swollman case SOPT_SET: 118938482Swollman switch (sopt->sopt_name) { 119038482Swollman /* XXX why are these shorts on get and ints on set? 119138482Swollman that doesn't make any sense... */ 119211819Sjulian case SO_HEADERS_ON_INPUT: 119311819Sjulian mask = SF_HI; 119411819Sjulian goto set_head; 119511819Sjulian 119611819Sjulian case SO_HEADERS_ON_OUTPUT: 119711819Sjulian mask = SF_HO; 119811819Sjulian set_head: 119938482Swollman error = sooptcopyin(sopt, &optval, sizeof optval, 120038482Swollman sizeof optval); 120138482Swollman if (error) 120238482Swollman break; 120338482Swollman 1204139932Srwatson IPX_LOCK(ipxp); 120511819Sjulian if (cb->s_flags & SF_PI) { 120638482Swollman if (optval) 120711819Sjulian cb->s_flags |= mask; 120811819Sjulian else 120911819Sjulian cb->s_flags &= ~mask; 121011819Sjulian } else error = EINVAL; 1211139932Srwatson IPX_UNLOCK(ipxp); 121211819Sjulian break; 121311819Sjulian 121411819Sjulian case SO_MTU: 121538482Swollman error = sooptcopyin(sopt, &usoptval, sizeof usoptval, 121638482Swollman sizeof usoptval); 121738482Swollman if (error) 121838482Swollman break; 1219139932Srwatson /* Unlocked write. */ 122038482Swollman cb->s_mtu = usoptval; 122111819Sjulian break; 122211819Sjulian 122311819Sjulian#ifdef SF_NEWCALL 122411819Sjulian case SO_NEWCALL: 122538482Swollman error = sooptcopyin(sopt, &optval, sizeof optval, 122638482Swollman sizeof optval); 122738482Swollman if (error) 122838482Swollman break; 1229139932Srwatson IPX_LOCK(ipxp); 123038482Swollman if (optval) { 123111819Sjulian cb->s_flags2 |= SF_NEWCALL; 123211819Sjulian spx_newchecks[5]++; 123311819Sjulian } else { 123411819Sjulian cb->s_flags2 &= ~SF_NEWCALL; 123511819Sjulian spx_newchecks[6]++; 123611819Sjulian } 1237139932Srwatson IPX_UNLOCK(ipxp); 123811819Sjulian break; 123911819Sjulian#endif 124011819Sjulian 124111819Sjulian case SO_DEFAULT_HEADERS: 124211819Sjulian { 124338482Swollman struct spxhdr sp; 124438482Swollman 124538482Swollman error = sooptcopyin(sopt, &sp, sizeof sp, 124638482Swollman sizeof sp); 124738482Swollman if (error) 124838482Swollman break; 1249139932Srwatson IPX_LOCK(ipxp); 125038482Swollman cb->s_dt = sp.spx_dt; 125138482Swollman cb->s_cc = sp.spx_cc & SPX_EM; 1252139932Srwatson IPX_UNLOCK(ipxp); 125311819Sjulian } 125411819Sjulian break; 125511819Sjulian 125611819Sjulian default: 125738482Swollman error = ENOPROTOOPT; 125811819Sjulian } 125911819Sjulian break; 126011819Sjulian } 126138482Swollman return (error); 126211819Sjulian} 126311819Sjulian 126424659Sjhaystatic int 1265157067Srwatsonspx_usr_abort(struct socket *so) 126611819Sjulian{ 126724659Sjhay struct ipxpcb *ipxp; 126824659Sjhay struct spxpcb *cb; 126911819Sjulian 127024659Sjhay ipxp = sotoipxpcb(so); 127124659Sjhay cb = ipxtospxpcb(ipxp); 127211819Sjulian 1273139932Srwatson IPX_LIST_LOCK(); 1274139932Srwatson IPX_LOCK(ipxp); 127524659Sjhay spx_drop(cb, ECONNABORTED); 1276139932Srwatson IPX_LIST_UNLOCK(); 127724659Sjhay return (0); 127824659Sjhay} 127911819Sjulian 128024659Sjhay/* 128124659Sjhay * Accept a connection. Essentially all the work is 128224659Sjhay * done at higher levels; just return the address 128324659Sjhay * of the peer, storing through addr. 128424659Sjhay */ 128524659Sjhaystatic int 1286157067Srwatsonspx_accept(struct socket *so, struct sockaddr **nam) 128724659Sjhay{ 128824659Sjhay struct ipxpcb *ipxp; 128928270Swollman struct sockaddr_ipx *sipx, ssipx; 129011819Sjulian 129124659Sjhay ipxp = sotoipxpcb(so); 129228270Swollman sipx = &ssipx; 129328270Swollman bzero(sipx, sizeof *sipx); 129428270Swollman sipx->sipx_len = sizeof *sipx; 129524659Sjhay sipx->sipx_family = AF_IPX; 1296139932Srwatson IPX_LOCK(ipxp); 129724659Sjhay sipx->sipx_addr = ipxp->ipxp_faddr; 1298139932Srwatson IPX_UNLOCK(ipxp); 1299139932Srwatson *nam = sodupsockaddr((struct sockaddr *)sipx, M_WAITOK); 130024659Sjhay return (0); 130124659Sjhay} 130224659Sjhay 130324659Sjhaystatic int 1304157067Srwatsonspx_attach(struct socket *so, int proto, struct thread *td) 130524659Sjhay{ 130624659Sjhay struct ipxpcb *ipxp; 130724659Sjhay struct spxpcb *cb; 130824659Sjhay struct mbuf *mm; 130924659Sjhay struct sockbuf *sb; 1310139932Srwatson int error; 131124659Sjhay 131224659Sjhay ipxp = sotoipxpcb(so); 131324659Sjhay cb = ipxtospxpcb(ipxp); 131424659Sjhay 1315139932Srwatson IPX_LIST_LOCK(); 1316139444Srwatson error = ipx_pcballoc(so, &ipxpcb_list, td); 131724659Sjhay if (error) 131824659Sjhay goto spx_attach_end; 131924659Sjhay if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 132024659Sjhay error = soreserve(so, (u_long) 3072, (u_long) 3072); 132111819Sjulian if (error) 132224659Sjhay goto spx_attach_end; 132324659Sjhay } 132424659Sjhay ipxp = sotoipxpcb(so); 132511819Sjulian 132669781Sdwmalone MALLOC(cb, struct spxpcb *, sizeof *cb, M_PCB, M_NOWAIT | M_ZERO); 132711819Sjulian 132828270Swollman if (cb == NULL) { 132924659Sjhay error = ENOBUFS; 133024659Sjhay goto spx_attach_end; 133124659Sjhay } 133243711Sjhay sb = &so->so_snd; 133328270Swollman 1334151967Sandre mm = m_getclr(M_DONTWAIT, MT_DATA); 133524659Sjhay if (mm == NULL) { 133628270Swollman FREE(cb, M_PCB); 133724659Sjhay error = ENOBUFS; 133824659Sjhay goto spx_attach_end; 133924659Sjhay } 134024659Sjhay cb->s_ipx = mtod(mm, struct ipx *); 134124659Sjhay cb->s_state = TCPS_LISTEN; 134224659Sjhay cb->s_smax = -1; 134324659Sjhay cb->s_swl1 = -1; 134424659Sjhay cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q; 134524659Sjhay cb->s_ipxpcb = ipxp; 134625652Sjhay cb->s_mtu = 576 - sizeof(struct spx); 134724659Sjhay cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu; 134824659Sjhay cb->s_ssthresh = cb->s_cwnd; 134925652Sjhay cb->s_cwmx = sbspace(sb) * CUNIT / (2 * sizeof(struct spx)); 135024659Sjhay /* Above is recomputed when connecting to account 135124659Sjhay for changed buffering or mtu's */ 135224659Sjhay cb->s_rtt = SPXTV_SRTTBASE; 135324659Sjhay cb->s_rttvar = SPXTV_SRTTDFLT << 2; 135424659Sjhay SPXT_RANGESET(cb->s_rxtcur, 135524659Sjhay ((SPXTV_SRTTBASE >> 2) + (SPXTV_SRTTDFLT << 2)) >> 1, 135624659Sjhay SPXTV_MIN, SPXTV_REXMTMAX); 1357139584Srwatson ipxp->ipxp_pcb = (caddr_t)cb; 135824659Sjhayspx_attach_end: 1359139932Srwatson IPX_LIST_UNLOCK(); 136024659Sjhay return (error); 136124659Sjhay} 136211819Sjulian 136324659Sjhaystatic int 1364157067Srwatsonspx_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 1365139584Srwatson{ 136624659Sjhay struct ipxpcb *ipxp; 1367139932Srwatson int error; 136811819Sjulian 136924659Sjhay ipxp = sotoipxpcb(so); 137011819Sjulian 1371139932Srwatson IPX_LIST_LOCK(); 1372139932Srwatson IPX_LOCK(ipxp); 1373139932Srwatson error = ipx_pcbbind(ipxp, nam, td); 1374139932Srwatson IPX_UNLOCK(ipxp); 1375139932Srwatson IPX_LIST_UNLOCK(); 1376139932Srwatson return (error); 1377139584Srwatson} 1378139584Srwatson 137924659Sjhay/* 138024659Sjhay * Initiate connection to peer. 138124659Sjhay * Enter SYN_SENT state, and mark socket as connecting. 138224659Sjhay * Start keep-alive timer, setup prototype header, 138324659Sjhay * Send initial system packet requesting connection. 138424659Sjhay */ 138524659Sjhaystatic int 1386157067Srwatsonspx_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 138724659Sjhay{ 138824659Sjhay struct ipxpcb *ipxp; 138924659Sjhay struct spxpcb *cb; 1390139932Srwatson int error; 139111819Sjulian 139224659Sjhay ipxp = sotoipxpcb(so); 139324659Sjhay cb = ipxtospxpcb(ipxp); 139424659Sjhay 1395139932Srwatson IPX_LIST_LOCK(); 1396139932Srwatson IPX_LOCK(ipxp); 139724659Sjhay if (ipxp->ipxp_lport == 0) { 1398139579Srwatson error = ipx_pcbbind(ipxp, NULL, td); 139924659Sjhay if (error) 140024659Sjhay goto spx_connect_end; 140124659Sjhay } 140283366Sjulian error = ipx_pcbconnect(ipxp, nam, td); 140324659Sjhay if (error) 140424659Sjhay goto spx_connect_end; 140524659Sjhay soisconnecting(so); 140624659Sjhay spxstat.spxs_connattempt++; 140724659Sjhay cb->s_state = TCPS_SYN_SENT; 140824659Sjhay cb->s_did = 0; 140924659Sjhay spx_template(cb); 141024659Sjhay cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 141124659Sjhay cb->s_force = 1 + SPXTV_KEEP; 141211819Sjulian /* 141324659Sjhay * Other party is required to respond to 141424659Sjhay * the port I send from, but he is not 141524659Sjhay * required to answer from where I am sending to, 141624659Sjhay * so allow wildcarding. 141724659Sjhay * original port I am sending to is still saved in 141824659Sjhay * cb->s_dport. 141911819Sjulian */ 142024659Sjhay ipxp->ipxp_fport = 0; 1421139579Srwatson error = spx_output(cb, NULL); 142224659Sjhayspx_connect_end: 1423139932Srwatson IPX_UNLOCK(ipxp); 1424139932Srwatson IPX_LIST_UNLOCK(); 142524659Sjhay return (error); 142624659Sjhay} 142711819Sjulian 142824659Sjhaystatic int 1429157067Srwatsonspx_detach(struct socket *so) 143024659Sjhay{ 143124659Sjhay struct ipxpcb *ipxp; 143224659Sjhay struct spxpcb *cb; 143311819Sjulian 143424659Sjhay ipxp = sotoipxpcb(so); 143524659Sjhay cb = ipxtospxpcb(ipxp); 143611819Sjulian 1437139932Srwatson IPX_LIST_LOCK(); 1438139932Srwatson IPX_LOCK(ipxp); 143924659Sjhay if (cb->s_state > TCPS_LISTEN) 144024659Sjhay spx_disconnect(cb); 144124659Sjhay else 144224659Sjhay spx_close(cb); 1443139932Srwatson IPX_LIST_UNLOCK(); 144424659Sjhay return (0); 144524659Sjhay} 144611819Sjulian 144724659Sjhay/* 144824659Sjhay * We may decide later to implement connection closing 144924659Sjhay * handshaking at the spx level optionally. 145024659Sjhay * here is the hook to do it: 145124659Sjhay */ 145224659Sjhaystatic int 1453157067Srwatsonspx_usr_disconnect(struct socket *so) 145424659Sjhay{ 145524659Sjhay struct ipxpcb *ipxp; 145624659Sjhay struct spxpcb *cb; 145711819Sjulian 145824659Sjhay ipxp = sotoipxpcb(so); 145924659Sjhay cb = ipxtospxpcb(ipxp); 146011819Sjulian 1461139932Srwatson IPX_LIST_LOCK(); 1462139932Srwatson IPX_LOCK(ipxp); 146324659Sjhay spx_disconnect(cb); 1464139932Srwatson IPX_LIST_UNLOCK(); 146524659Sjhay return (0); 146624659Sjhay} 146711819Sjulian 146824659Sjhaystatic int 1469157067Srwatsonspx_listen(struct socket *so, int backlog, struct thread *td) 147024659Sjhay{ 147124659Sjhay int error; 147224659Sjhay struct ipxpcb *ipxp; 147324659Sjhay struct spxpcb *cb; 147411819Sjulian 147524659Sjhay error = 0; 147624659Sjhay ipxp = sotoipxpcb(so); 147724659Sjhay cb = ipxtospxpcb(ipxp); 147811819Sjulian 1479139932Srwatson IPX_LIST_LOCK(); 1480139932Srwatson IPX_LOCK(ipxp); 1481142190Srwatson SOCK_LOCK(so); 1482142190Srwatson error = solisten_proto_check(so); 1483142190Srwatson if (error == 0 && ipxp->ipxp_lport == 0) 1484139579Srwatson error = ipx_pcbbind(ipxp, NULL, td); 1485142190Srwatson if (error == 0) { 148624659Sjhay cb->s_state = TCPS_LISTEN; 1487151888Srwatson solisten_proto(so, backlog); 1488142190Srwatson } 1489142190Srwatson SOCK_UNLOCK(so); 1490139932Srwatson IPX_UNLOCK(ipxp); 1491139932Srwatson IPX_LIST_UNLOCK(); 149224659Sjhay return (error); 149324659Sjhay} 149411819Sjulian 149524659Sjhay/* 149624659Sjhay * After a receive, possibly send acknowledgment 149724659Sjhay * updating allocation. 149824659Sjhay */ 149924659Sjhaystatic int 1500157067Srwatsonspx_rcvd(struct socket *so, int flags) 150124659Sjhay{ 150224659Sjhay struct ipxpcb *ipxp; 150324659Sjhay struct spxpcb *cb; 150411819Sjulian 150524659Sjhay ipxp = sotoipxpcb(so); 150624659Sjhay cb = ipxtospxpcb(ipxp); 150711819Sjulian 1508139932Srwatson IPX_LOCK(ipxp); 150924659Sjhay cb->s_flags |= SF_RVD; 1510139579Srwatson spx_output(cb, NULL); 151124659Sjhay cb->s_flags &= ~SF_RVD; 1512139932Srwatson IPX_UNLOCK(ipxp); 151324659Sjhay return (0); 151424659Sjhay} 151511819Sjulian 151624659Sjhaystatic int 1517157067Srwatsonspx_rcvoob(struct socket *so, struct mbuf *m, int flags) 151824659Sjhay{ 151924659Sjhay struct ipxpcb *ipxp; 152024659Sjhay struct spxpcb *cb; 152111819Sjulian 152224659Sjhay ipxp = sotoipxpcb(so); 152324659Sjhay cb = ipxtospxpcb(ipxp); 152411819Sjulian 1525139591Srwatson SOCKBUF_LOCK(&so->so_rcv); 152624659Sjhay if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark || 1527130480Srwatson (so->so_rcv.sb_state & SBS_RCVATMARK)) { 1528139591Srwatson SOCKBUF_UNLOCK(&so->so_rcv); 152924659Sjhay m->m_len = 1; 1530139932Srwatson /* Unlocked read. */ 153124659Sjhay *mtod(m, caddr_t) = cb->s_iobc; 153224659Sjhay return (0); 153311819Sjulian } 1534139591Srwatson SOCKBUF_UNLOCK(&so->so_rcv); 153524659Sjhay return (EINVAL); 153624659Sjhay} 153724659Sjhay 153824659Sjhaystatic int 1539157067Srwatsonspx_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, 1540157067Srwatson struct mbuf *controlp, struct thread *td) 154124659Sjhay{ 154224659Sjhay int error; 154324659Sjhay struct ipxpcb *ipxp; 154424659Sjhay struct spxpcb *cb; 154524659Sjhay 154624659Sjhay error = 0; 154724659Sjhay ipxp = sotoipxpcb(so); 154824659Sjhay cb = ipxtospxpcb(ipxp); 154924659Sjhay 1550139932Srwatson IPX_LOCK(ipxp); 155124659Sjhay if (flags & PRUS_OOB) { 155224659Sjhay if (sbspace(&so->so_snd) < -512) { 155324659Sjhay error = ENOBUFS; 155424659Sjhay goto spx_send_end; 155524659Sjhay } 155624659Sjhay cb->s_oobflags |= SF_SOOB; 155724659Sjhay } 155825652Sjhay if (controlp != NULL) { 155924659Sjhay u_short *p = mtod(controlp, u_short *); 156024659Sjhay spx_newchecks[2]++; 156125652Sjhay if ((p[0] == 5) && (p[1] == 1)) { /* XXXX, for testing */ 156224659Sjhay cb->s_shdr.spx_dt = *(u_char *)(&p[2]); 156324659Sjhay spx_newchecks[3]++; 156424659Sjhay } 156524659Sjhay m_freem(controlp); 156624659Sjhay } 156724659Sjhay controlp = NULL; 156824659Sjhay error = spx_output(cb, m); 156924659Sjhay m = NULL; 157024659Sjhayspx_send_end: 1571139932Srwatson IPX_UNLOCK(ipxp); 157211819Sjulian if (controlp != NULL) 157311819Sjulian m_freem(controlp); 157411819Sjulian if (m != NULL) 157511819Sjulian m_freem(m); 157611819Sjulian return (error); 157711819Sjulian} 157811819Sjulian 157924659Sjhaystatic int 1580157067Srwatsonspx_shutdown(struct socket *so) 158124659Sjhay{ 158224659Sjhay struct ipxpcb *ipxp; 158324659Sjhay struct spxpcb *cb; 158424659Sjhay 158524659Sjhay ipxp = sotoipxpcb(so); 158624659Sjhay cb = ipxtospxpcb(ipxp); 158724659Sjhay 158824659Sjhay socantsendmore(so); 1589139932Srwatson IPX_LIST_LOCK(); 1590139932Srwatson IPX_LOCK(ipxp); 1591139931Srwatson spx_usrclosed(cb); 1592139932Srwatson IPX_LIST_UNLOCK(); 1593139931Srwatson return (0); 159424659Sjhay} 159524659Sjhay 159624659Sjhaystatic int 1597157067Srwatsonspx_sp_attach(struct socket *so, int proto, struct thread *td) 159811819Sjulian{ 159924659Sjhay int error; 160024659Sjhay struct ipxpcb *ipxp; 160111819Sjulian 160283366Sjulian error = spx_attach(so, proto, td); 160324659Sjhay if (error == 0) { 160424659Sjhay ipxp = sotoipxpcb(so); 160511819Sjulian ((struct spxpcb *)ipxp->ipxp_pcb)->s_flags |= 160611819Sjulian (SF_HI | SF_HO | SF_PI); 160711819Sjulian } 160811819Sjulian return (error); 160911819Sjulian} 161011819Sjulian 161111819Sjulian/* 161211819Sjulian * Create template to be used to send spx packets on a connection. 161311819Sjulian * Called after host entry created, fills 161411819Sjulian * in a skeletal spx header (choosing connection id), 161511819Sjulian * minimizing the amount of work necessary when the connection is used. 161611819Sjulian */ 161725652Sjhaystatic void 1618157067Srwatsonspx_template(struct spxpcb *cb) 161911819Sjulian{ 1620157067Srwatson struct ipxpcb *ipxp = cb->s_ipxpcb; 1621157067Srwatson struct ipx *ipx = cb->s_ipx; 1622157067Srwatson struct sockbuf *sb = &(ipxp->ipxp_socket->so_snd); 162311819Sjulian 1624139932Srwatson IPX_LOCK_ASSERT(ipxp); 1625139932Srwatson 162611819Sjulian ipx->ipx_pt = IPXPROTO_SPX; 162711819Sjulian ipx->ipx_sna = ipxp->ipxp_laddr; 162811819Sjulian ipx->ipx_dna = ipxp->ipxp_faddr; 162911819Sjulian cb->s_sid = htons(spx_iss); 163011819Sjulian spx_iss += SPX_ISSINCR/2; 163111819Sjulian cb->s_alo = 1; 163211819Sjulian cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu; 163311819Sjulian cb->s_ssthresh = cb->s_cwnd; /* Try to expand fast to full complement 163411819Sjulian of large packets */ 163511819Sjulian cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spx)); 163611819Sjulian cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd); 163711819Sjulian /* But allow for lots of little packets as well */ 163811819Sjulian} 163911819Sjulian 164011819Sjulian/* 164111819Sjulian * Close a SPIP control block: 164211819Sjulian * discard spx control block itself 164311819Sjulian * discard ipx protocol control block 164411819Sjulian * wake up any sleepers 1645139931Srwatson * cb will always be invalid after this call. 164611819Sjulian */ 1647139931Srwatsonvoid 1648157067Srwatsonspx_close(struct spxpcb *cb) 164911819Sjulian{ 1650157067Srwatson struct spx_q *s; 165111819Sjulian struct ipxpcb *ipxp = cb->s_ipxpcb; 165211819Sjulian struct socket *so = ipxp->ipxp_socket; 1653157067Srwatson struct mbuf *m; 165411819Sjulian 1655139932Srwatson IPX_LIST_LOCK_ASSERT(); 1656139932Srwatson IPX_LOCK_ASSERT(ipxp); 1657139932Srwatson 165811819Sjulian s = cb->s_q.si_next; 165911819Sjulian while (s != &(cb->s_q)) { 166011819Sjulian s = s->si_next; 166111819Sjulian m = dtom(s->si_prev); 166211819Sjulian remque(s->si_prev); 166311819Sjulian m_freem(m); 166411819Sjulian } 166525652Sjhay m_free(dtom(cb->s_ipx)); 166628270Swollman FREE(cb, M_PCB); 1667139580Srwatson ipxp->ipxp_pcb = NULL; 166811819Sjulian soisdisconnected(so); 166911819Sjulian ipx_pcbdetach(ipxp); 167011819Sjulian spxstat.spxs_closed++; 167111819Sjulian} 167225652Sjhay 167311819Sjulian/* 167411819Sjulian * Someday we may do level 3 handshaking 167511819Sjulian * to close a connection or send a xerox style error. 167611819Sjulian * For now, just close. 1677139931Srwatson * cb will always be invalid after this call. 167811819Sjulian */ 1679139931Srwatsonstatic void 1680157067Srwatsonspx_usrclosed(struct spxpcb *cb) 168111819Sjulian{ 1682139931Srwatson 1683139932Srwatson IPX_LIST_LOCK_ASSERT(); 1684139932Srwatson IPX_LOCK_ASSERT(cb->s_ipxpcb); 1685139932Srwatson 1686139931Srwatson spx_close(cb); 168711819Sjulian} 168825652Sjhay 1689139931Srwatson/* 1690139931Srwatson * cb will always be invalid after this call. 1691139931Srwatson */ 1692139931Srwatsonstatic void 1693157067Srwatsonspx_disconnect(struct spxpcb *cb) 169411819Sjulian{ 1695139931Srwatson 1696139932Srwatson IPX_LIST_LOCK_ASSERT(); 1697139932Srwatson IPX_LOCK_ASSERT(cb->s_ipxpcb); 1698139932Srwatson 1699139931Srwatson spx_close(cb); 170011819Sjulian} 170125652Sjhay 170211819Sjulian/* 170311819Sjulian * Drop connection, reporting 170411819Sjulian * the specified error. 1705139931Srwatson * cb will always be invalid after this call. 170611819Sjulian */ 1707139931Srwatsonstatic void 1708157067Srwatsonspx_drop(struct spxpcb *cb, int errno) 170911819Sjulian{ 171011819Sjulian struct socket *so = cb->s_ipxpcb->ipxp_socket; 171111819Sjulian 1712139932Srwatson IPX_LIST_LOCK_ASSERT(); 1713139932Srwatson IPX_LOCK_ASSERT(cb->s_ipxpcb); 1714139932Srwatson 171511819Sjulian /* 171611819Sjulian * someday, in the xerox world 171711819Sjulian * we will generate error protocol packets 171811819Sjulian * announcing that the socket has gone away. 171911819Sjulian */ 172011819Sjulian if (TCPS_HAVERCVDSYN(cb->s_state)) { 172111819Sjulian spxstat.spxs_drops++; 172211819Sjulian cb->s_state = TCPS_CLOSED; 172325652Sjhay /*tcp_output(cb);*/ 172411819Sjulian } else 172511819Sjulian spxstat.spxs_conndrops++; 172611819Sjulian so->so_error = errno; 1727139931Srwatson spx_close(cb); 172811819Sjulian} 172911819Sjulian 173011819Sjulian/* 173111819Sjulian * Fast timeout routine for processing delayed acks 173211819Sjulian */ 173311819Sjulianvoid 1734157067Srwatsonspx_fasttimo(void) 173511819Sjulian{ 1736139932Srwatson struct ipxpcb *ipxp; 1737139932Srwatson struct spxpcb *cb; 173811819Sjulian 1739139932Srwatson IPX_LIST_LOCK(); 1740139444Srwatson LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) { 1741139932Srwatson IPX_LOCK(ipxp); 174225652Sjhay if ((cb = (struct spxpcb *)ipxp->ipxp_pcb) != NULL && 174311819Sjulian (cb->s_flags & SF_DELACK)) { 174411819Sjulian cb->s_flags &= ~SF_DELACK; 174511819Sjulian cb->s_flags |= SF_ACKNOW; 174611819Sjulian spxstat.spxs_delack++; 1747139579Srwatson spx_output(cb, NULL); 174811819Sjulian } 1749139932Srwatson IPX_UNLOCK(ipxp); 1750139444Srwatson } 1751139932Srwatson IPX_LIST_UNLOCK(); 175211819Sjulian} 175311819Sjulian 175411819Sjulian/* 175511819Sjulian * spx protocol timeout routine called every 500 ms. 175611819Sjulian * Updates the timers in all active pcb's and 175711819Sjulian * causes finite state machine actions if timers expire. 175811819Sjulian */ 175911819Sjulianvoid 1760157067Srwatsonspx_slowtimo(void) 176111819Sjulian{ 1762139932Srwatson struct ipxpcb *ip, *ip_temp; 1763139932Srwatson struct spxpcb *cb; 1764139932Srwatson int i; 176511819Sjulian 176611819Sjulian /* 1767139444Srwatson * Search through tcb's and update active timers. Note that timers 1768139444Srwatson * may free the ipxpcb, so be sure to handle that case. 1769139932Srwatson * 1770139932Srwatson * spx_timers() may remove an ipxpcb entry, so we have to be ready to 1771139932Srwatson * continue despite that. The logic here is a bit obfuscated. 177211819Sjulian */ 1773139932Srwatson IPX_LIST_LOCK(); 1774139444Srwatson LIST_FOREACH_SAFE(ip, &ipxpcb_list, ipxp_list, ip_temp) { 177511819Sjulian cb = ipxtospxpcb(ip); 177625652Sjhay if (cb == NULL) 1777139444Srwatson continue; 1778139932Srwatson IPX_LOCK(cb->s_ipxpcb); 177911819Sjulian for (i = 0; i < SPXT_NTIMERS; i++) { 178011819Sjulian if (cb->s_timer[i] && --cb->s_timer[i] == 0) { 1781139444Srwatson /* 1782139444Srwatson * spx_timers() returns (NULL) if it free'd 1783139444Srwatson * the pcb. 1784139444Srwatson */ 1785139581Srwatson cb = spx_timers(cb, i); 1786139581Srwatson if (cb == NULL) 1787139581Srwatson break; 178811819Sjulian } 178911819Sjulian } 1790139581Srwatson if (cb != NULL) { 1791139581Srwatson cb->s_idle++; 1792139581Srwatson if (cb->s_rtt) 1793139581Srwatson cb->s_rtt++; 1794139932Srwatson IPX_UNLOCK(cb->s_ipxpcb); 1795139581Srwatson } 179611819Sjulian } 179711819Sjulian spx_iss += SPX_ISSINCR/PR_SLOWHZ; /* increment iss */ 1798139932Srwatson IPX_LIST_UNLOCK(); 179911819Sjulian} 180025652Sjhay 180111819Sjulian/* 180211819Sjulian * SPX timer processing. 180311819Sjulian */ 180425652Sjhaystatic struct spxpcb * 1805157067Srwatsonspx_timers(struct spxpcb *cb, int timer) 180611819Sjulian{ 180711819Sjulian long rexmt; 180811819Sjulian int win; 180911819Sjulian 1810139932Srwatson IPX_LIST_LOCK_ASSERT(); 1811139932Srwatson IPX_LOCK_ASSERT(cb->s_ipxpcb); 1812139932Srwatson 181311819Sjulian cb->s_force = 1 + timer; 181411819Sjulian switch (timer) { 181511819Sjulian 181611819Sjulian /* 181711819Sjulian * 2 MSL timeout in shutdown went off. TCP deletes connection 181811819Sjulian * control block. 181911819Sjulian */ 182011819Sjulian case SPXT_2MSL: 182111819Sjulian printf("spx: SPXT_2MSL went off for no reason\n"); 182211819Sjulian cb->s_timer[timer] = 0; 182311819Sjulian break; 182411819Sjulian 182511819Sjulian /* 182611819Sjulian * Retransmission timer went off. Message has not 182711819Sjulian * been acked within retransmit interval. Back off 182811819Sjulian * to a longer retransmit interval and retransmit one packet. 182911819Sjulian */ 183011819Sjulian case SPXT_REXMT: 183111819Sjulian if (++cb->s_rxtshift > SPX_MAXRXTSHIFT) { 183211819Sjulian cb->s_rxtshift = SPX_MAXRXTSHIFT; 183311819Sjulian spxstat.spxs_timeoutdrop++; 1834139931Srwatson spx_drop(cb, ETIMEDOUT); 1835139931Srwatson cb = NULL; 183611819Sjulian break; 183711819Sjulian } 183811819Sjulian spxstat.spxs_rexmttimeo++; 183911819Sjulian rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1; 184011819Sjulian rexmt *= spx_backoff[cb->s_rxtshift]; 184111819Sjulian SPXT_RANGESET(cb->s_rxtcur, rexmt, SPXTV_MIN, SPXTV_REXMTMAX); 184211819Sjulian cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 184311819Sjulian /* 184411819Sjulian * If we have backed off fairly far, our srtt 184511819Sjulian * estimate is probably bogus. Clobber it 184611819Sjulian * so we'll take the next rtt measurement as our srtt; 184711819Sjulian * move the current srtt into rttvar to keep the current 184811819Sjulian * retransmit times until then. 184911819Sjulian */ 185011819Sjulian if (cb->s_rxtshift > SPX_MAXRXTSHIFT / 4 ) { 185111819Sjulian cb->s_rttvar += (cb->s_srtt >> 2); 185211819Sjulian cb->s_srtt = 0; 185311819Sjulian } 185411819Sjulian cb->s_snxt = cb->s_rack; 185511819Sjulian /* 185611819Sjulian * If timing a packet, stop the timer. 185711819Sjulian */ 185811819Sjulian cb->s_rtt = 0; 185911819Sjulian /* 186011819Sjulian * See very long discussion in tcp_timer.c about congestion 186111819Sjulian * window and sstrhesh 186211819Sjulian */ 186311819Sjulian win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2; 186411819Sjulian if (win < 2) 186511819Sjulian win = 2; 186611819Sjulian cb->s_cwnd = CUNIT; 186711819Sjulian cb->s_ssthresh = win * CUNIT; 1868139579Srwatson spx_output(cb, NULL); 186911819Sjulian break; 187011819Sjulian 187111819Sjulian /* 187211819Sjulian * Persistance timer into zero window. 187311819Sjulian * Force a probe to be sent. 187411819Sjulian */ 187511819Sjulian case SPXT_PERSIST: 187611819Sjulian spxstat.spxs_persisttimeo++; 187711819Sjulian spx_setpersist(cb); 1878139579Srwatson spx_output(cb, NULL); 187911819Sjulian break; 188011819Sjulian 188111819Sjulian /* 188211819Sjulian * Keep-alive timer went off; send something 188311819Sjulian * or drop connection if idle for too long. 188411819Sjulian */ 188511819Sjulian case SPXT_KEEP: 188611819Sjulian spxstat.spxs_keeptimeo++; 188711819Sjulian if (cb->s_state < TCPS_ESTABLISHED) 188811819Sjulian goto dropit; 188911819Sjulian if (cb->s_ipxpcb->ipxp_socket->so_options & SO_KEEPALIVE) { 189011819Sjulian if (cb->s_idle >= SPXTV_MAXIDLE) 189111819Sjulian goto dropit; 189211819Sjulian spxstat.spxs_keepprobe++; 1893139579Srwatson spx_output(cb, NULL); 189497658Stanimura } else 189511819Sjulian cb->s_idle = 0; 189611819Sjulian cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 189711819Sjulian break; 189811819Sjulian dropit: 189911819Sjulian spxstat.spxs_keepdrops++; 1900139931Srwatson spx_drop(cb, ETIMEDOUT); 1901139931Srwatson cb = NULL; 190211819Sjulian break; 190311819Sjulian } 190411819Sjulian return (cb); 190511819Sjulian} 1906