spx_usrreq.c revision 139485
1170222Sdougb/* 2254402Serwin * Copyright (c) 2004 Robert N. M. Watson 3170222Sdougb * Copyright (c) 1995, Mike Mitchell 4186462Sdougb * Copyright (c) 1984, 1985, 1986, 1987, 1993 5170222Sdougb * The Regents of the University of California. All rights reserved. 6170222Sdougb * 7170222Sdougb * Redistribution and use in source and binary forms, with or without 8170222Sdougb * modification, are permitted provided that the following conditions 9170222Sdougb * are met: 10170222Sdougb * 1. Redistributions of source code must retain the above copyright 11170222Sdougb * notice, this list of conditions and the following disclaimer. 12170222Sdougb * 2. Redistributions in binary form must reproduce the above copyright 13170222Sdougb * notice, this list of conditions and the following disclaimer in the 14170222Sdougb * documentation and/or other materials provided with the distribution. 15170222Sdougb * 3. All advertising materials mentioning features or use of this software 16170222Sdougb * must display the following acknowledgement: 17234010Sdougb * This product includes software developed by the University of 18170222Sdougb * California, Berkeley and its contributors. 19170222Sdougb * 4. Neither the name of the University nor the names of its contributors 20170222Sdougb * may be used to endorse or promote products derived from this software 21170222Sdougb * without specific prior written permission. 22170222Sdougb * 23170222Sdougb * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24170222Sdougb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25170222Sdougb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26170222Sdougb * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27170222Sdougb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28170222Sdougb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29170222Sdougb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30254402Serwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31170222Sdougb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32170222Sdougb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33170222Sdougb * SUCH DAMAGE. 34170222Sdougb * 35170222Sdougb * @(#)spx_usrreq.h 36170222Sdougb */ 37170222Sdougb 38170222Sdougb#include <sys/cdefs.h> 39170222Sdougb__FBSDID("$FreeBSD: head/sys/netipx/spx_usrreq.c 139485 2004-12-31 17:05:37Z rwatson $"); 40170222Sdougb 41170222Sdougb#include <sys/param.h> 42170222Sdougb#include <sys/lock.h> 43170222Sdougb#include <sys/malloc.h> 44170222Sdougb#include <sys/mbuf.h> 45170222Sdougb#include <sys/mutex.h> 46170222Sdougb#include <sys/proc.h> 47170222Sdougb#include <sys/protosw.h> 48170222Sdougb#include <sys/signalvar.h> 49170222Sdougb#include <sys/socket.h> 50170222Sdougb#include <sys/socketvar.h> 51170222Sdougb#include <sys/sx.h> 52170222Sdougb#include <sys/systm.h> 53170222Sdougb 54170222Sdougb#include <net/route.h> 55170222Sdougb#include <netinet/tcp_fsm.h> 56170222Sdougb 57170222Sdougb#include <netipx/ipx.h> 58170222Sdougb#include <netipx/ipx_pcb.h> 59170222Sdougb#include <netipx/ipx_var.h> 60170222Sdougb#include <netipx/spx.h> 61170222Sdougb#include <netipx/spx_debug.h> 62170222Sdougb#include <netipx/spx_timer.h> 63170222Sdougb#include <netipx/spx_var.h> 64170222Sdougb 65170222Sdougb/* 66170222Sdougb * SPX protocol implementation. 67170222Sdougb */ 68170222Sdougbstatic u_short spx_iss; 69170222Sdougbstatic u_short spx_newchecks[50]; 70170222Sdougbstatic int spx_hardnosed; 71170222Sdougbstatic int spx_use_delack = 0; 72170222Sdougbstatic int traceallspxs = 0; 73170222Sdougbstatic struct spx spx_savesi; 74170222Sdougbstatic struct spx_istat spx_istat; 75170222Sdougb 76254402Serwin/* Following was struct spxstat spxstat; */ 77254402Serwin#ifndef spxstat 78170222Sdougb#define spxstat spx_istat.newstats 79254402Serwin#endif 80170222Sdougb 81170222Sdougbstatic const int spx_backoff[SPX_MAXRXTSHIFT+1] = 82170222Sdougb { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 }; 83170222Sdougb 84170222Sdougbstatic struct spxpcb *spx_close(struct spxpcb *cb); 85170222Sdougbstatic struct spxpcb *spx_disconnect(struct spxpcb *cb); 86170222Sdougbstatic struct spxpcb *spx_drop(struct spxpcb *cb, int errno); 87170222Sdougbstatic int spx_output(struct spxpcb *cb, struct mbuf *m0); 88170222Sdougbstatic int spx_reass(struct spxpcb *cb, struct spx *si); 89170222Sdougbstatic void spx_setpersist(struct spxpcb *cb); 90170222Sdougbstatic void spx_template(struct spxpcb *cb); 91170222Sdougbstatic struct spxpcb *spx_timers(struct spxpcb *cb, int timer); 92170222Sdougbstatic struct spxpcb *spx_usrclosed(struct spxpcb *cb); 93170222Sdougb 94170222Sdougbstatic int spx_usr_abort(struct socket *so); 95170222Sdougbstatic int spx_accept(struct socket *so, struct sockaddr **nam); 96170222Sdougbstatic int spx_attach(struct socket *so, int proto, struct thread *td); 97170222Sdougbstatic int spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td); 98170222Sdougbstatic int spx_connect(struct socket *so, struct sockaddr *nam, 99170222Sdougb struct thread *td); 100170222Sdougbstatic int spx_detach(struct socket *so); 101170222Sdougbstatic int spx_usr_disconnect(struct socket *so); 102170222Sdougbstatic int spx_listen(struct socket *so, struct thread *td); 103170222Sdougbstatic int spx_rcvd(struct socket *so, int flags); 104170222Sdougbstatic int spx_rcvoob(struct socket *so, struct mbuf *m, int flags); 105170222Sdougbstatic int spx_send(struct socket *so, int flags, struct mbuf *m, 106170222Sdougb struct sockaddr *addr, struct mbuf *control, 107170222Sdougb struct thread *td); 108170222Sdougbstatic int spx_shutdown(struct socket *so); 109170222Sdougbstatic int spx_sp_attach(struct socket *so, int proto, struct thread *td); 110170222Sdougb 111170222Sdougbstruct pr_usrreqs spx_usrreqs = { 112170222Sdougb .pru_abort = spx_usr_abort, 113170222Sdougb .pru_accept = spx_accept, 114170222Sdougb .pru_attach = spx_attach, 115170222Sdougb .pru_bind = spx_bind, 116170222Sdougb .pru_connect = spx_connect, 117170222Sdougb .pru_control = ipx_control, 118170222Sdougb .pru_detach = spx_detach, 119170222Sdougb .pru_disconnect = spx_usr_disconnect, 120170222Sdougb .pru_listen = spx_listen, 121170222Sdougb .pru_peeraddr = ipx_peeraddr, 122170222Sdougb .pru_rcvd = spx_rcvd, 123170222Sdougb .pru_rcvoob = spx_rcvoob, 124170222Sdougb .pru_send = spx_send, 125170222Sdougb .pru_shutdown = spx_shutdown, 126170222Sdougb .pru_sockaddr = ipx_sockaddr, 127170222Sdougb}; 128170222Sdougb 129170222Sdougbstruct pr_usrreqs spx_usrreq_sps = { 130170222Sdougb .pru_abort = spx_usr_abort, 131170222Sdougb .pru_accept = spx_accept, 132170222Sdougb .pru_attach = spx_sp_attach, 133170222Sdougb .pru_bind = spx_bind, 134170222Sdougb .pru_connect = spx_connect, 135170222Sdougb .pru_control = ipx_control, 136170222Sdougb .pru_detach = spx_detach, 137170222Sdougb .pru_disconnect = spx_usr_disconnect, 138170222Sdougb .pru_listen = spx_listen, 139170222Sdougb .pru_peeraddr = ipx_peeraddr, 140170222Sdougb .pru_rcvd = spx_rcvd, 141186462Sdougb .pru_rcvoob = spx_rcvoob, 142170222Sdougb .pru_send = spx_send, 143170222Sdougb .pru_shutdown = spx_shutdown, 144170222Sdougb .pru_sockaddr = ipx_sockaddr, 145170222Sdougb}; 146170222Sdougb 147170222Sdougbvoid 148170222Sdougbspx_init() 149170222Sdougb{ 150170222Sdougb 151170222Sdougb spx_iss = 1; /* WRONG !! should fish it out of TODR */ 152170222Sdougb} 153170222Sdougb 154170222Sdougbvoid 155170222Sdougbspx_input(m, ipxp) 156170222Sdougb register struct mbuf *m; 157170222Sdougb register struct ipxpcb *ipxp; 158170222Sdougb{ 159170222Sdougb register struct spxpcb *cb; 160170222Sdougb register struct spx *si = mtod(m, struct spx *); 161170222Sdougb register struct socket *so; 162170222Sdougb int dropsocket = 0; 163170222Sdougb short ostate = 0; 164170222Sdougb 165170222Sdougb spxstat.spxs_rcvtotal++; 166170222Sdougb if (ipxp == NULL) { 167170222Sdougb panic("No ipxpcb in spx_input\n"); 168170222Sdougb return; 169170222Sdougb } 170170222Sdougb 171170222Sdougb cb = ipxtospxpcb(ipxp); 172170222Sdougb if (cb == NULL) 173170222Sdougb goto bad; 174170222Sdougb 175170222Sdougb if (m->m_len < sizeof(*si)) { 176170222Sdougb if ((m = m_pullup(m, sizeof(*si))) == NULL) { 177170222Sdougb spxstat.spxs_rcvshort++; 178170222Sdougb return; 179170222Sdougb } 180170222Sdougb si = mtod(m, struct spx *); 181170222Sdougb } 182170222Sdougb si->si_seq = ntohs(si->si_seq); 183170222Sdougb si->si_ack = ntohs(si->si_ack); 184170222Sdougb si->si_alo = ntohs(si->si_alo); 185170222Sdougb 186170222Sdougb so = ipxp->ipxp_socket; 187170222Sdougb 188170222Sdougb if (so->so_options & SO_DEBUG || traceallspxs) { 189170222Sdougb ostate = cb->s_state; 190170222Sdougb spx_savesi = *si; 191170222Sdougb } 192170222Sdougb if (so->so_options & SO_ACCEPTCONN) { 193170222Sdougb struct spxpcb *ocb = cb; 194170222Sdougb 195170222Sdougb so = sonewconn(so, 0); 196170222Sdougb if (so == NULL) { 197170222Sdougb goto drop; 198170222Sdougb } 199170222Sdougb /* 200170222Sdougb * This is ugly, but .... 201170222Sdougb * 202170222Sdougb * Mark socket as temporary until we're 203170222Sdougb * committed to keeping it. The code at 204170222Sdougb * ``drop'' and ``dropwithreset'' check the 205170222Sdougb * flag dropsocket to see if the temporary 206170222Sdougb * socket created here should be discarded. 207170222Sdougb * We mark the socket as discardable until 208170222Sdougb * we're committed to it below in TCPS_LISTEN. 209170222Sdougb */ 210170222Sdougb dropsocket++; 211170222Sdougb ipxp = (struct ipxpcb *)so->so_pcb; 212170222Sdougb ipxp->ipxp_laddr = si->si_dna; 213170222Sdougb cb = ipxtospxpcb(ipxp); 214170222Sdougb cb->s_mtu = ocb->s_mtu; /* preserve sockopts */ 215170222Sdougb cb->s_flags = ocb->s_flags; /* preserve sockopts */ 216170222Sdougb cb->s_flags2 = ocb->s_flags2; /* preserve sockopts */ 217170222Sdougb cb->s_state = TCPS_LISTEN; 218170222Sdougb } 219170222Sdougb 220170222Sdougb /* 221170222Sdougb * Packet received on connection. 222170222Sdougb * reset idle time and keep-alive timer; 223170222Sdougb */ 224170222Sdougb cb->s_idle = 0; 225170222Sdougb cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 226170222Sdougb 227170222Sdougb switch (cb->s_state) { 228170222Sdougb 229170222Sdougb case TCPS_LISTEN:{ 230170222Sdougb struct sockaddr_ipx *sipx, ssipx; 231170222Sdougb struct ipx_addr laddr; 232170222Sdougb 233170222Sdougb /* 234170222Sdougb * If somebody here was carying on a conversation 235170222Sdougb * and went away, and his pen pal thinks he can 236170222Sdougb * still talk, we get the misdirected packet. 237170222Sdougb */ 238170222Sdougb if (spx_hardnosed && (si->si_did != 0 || si->si_seq != 0)) { 239170222Sdougb spx_istat.gonawy++; 240170222Sdougb goto dropwithreset; 241170222Sdougb } 242170222Sdougb sipx = &ssipx; 243170222Sdougb bzero(sipx, sizeof *sipx); 244170222Sdougb sipx->sipx_len = sizeof(*sipx); 245170222Sdougb sipx->sipx_family = AF_IPX; 246170222Sdougb sipx->sipx_addr = si->si_sna; 247170222Sdougb laddr = ipxp->ipxp_laddr; 248170222Sdougb if (ipx_nullhost(laddr)) 249170222Sdougb ipxp->ipxp_laddr = si->si_dna; 250170222Sdougb if (ipx_pcbconnect(ipxp, (struct sockaddr *)sipx, &thread0)) { 251170222Sdougb ipxp->ipxp_laddr = laddr; 252170222Sdougb spx_istat.noconn++; 253170222Sdougb goto drop; 254170222Sdougb } 255170222Sdougb spx_template(cb); 256170222Sdougb dropsocket = 0; /* committed to socket */ 257170222Sdougb cb->s_did = si->si_sid; 258170222Sdougb cb->s_rack = si->si_ack; 259170222Sdougb cb->s_ralo = si->si_alo; 260170222Sdougb#define THREEWAYSHAKE 261170222Sdougb#ifdef THREEWAYSHAKE 262170222Sdougb cb->s_state = TCPS_SYN_RECEIVED; 263170222Sdougb cb->s_force = 1 + SPXT_KEEP; 264170222Sdougb spxstat.spxs_accepts++; 265170222Sdougb cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 266170222Sdougb } 267170222Sdougb break; 268170222Sdougb /* 269170222Sdougb * This state means that we have heard a response 270170222Sdougb * to our acceptance of their connection 271170222Sdougb * It is probably logically unnecessary in this 272170222Sdougb * implementation. 273170222Sdougb */ 274170222Sdougb case TCPS_SYN_RECEIVED: { 275170222Sdougb if (si->si_did != cb->s_sid) { 276170222Sdougb spx_istat.wrncon++; 277170222Sdougb goto drop; 278170222Sdougb } 279170222Sdougb#endif 280170222Sdougb ipxp->ipxp_fport = si->si_sport; 281170222Sdougb cb->s_timer[SPXT_REXMT] = 0; 282170222Sdougb cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 283170222Sdougb soisconnected(so); 284170222Sdougb cb->s_state = TCPS_ESTABLISHED; 285170222Sdougb spxstat.spxs_accepts++; 286170222Sdougb } 287170222Sdougb break; 288170222Sdougb 289170222Sdougb /* 290170222Sdougb * This state means that we have gotten a response 291170222Sdougb * to our attempt to establish a connection. 292170222Sdougb * We fill in the data from the other side, 293170222Sdougb * telling us which port to respond to, instead of the well- 294170222Sdougb * known one we might have sent to in the first place. 295170222Sdougb * We also require that this is a response to our 296170222Sdougb * connection id. 297170222Sdougb */ 298170222Sdougb case TCPS_SYN_SENT: 299170222Sdougb if (si->si_did != cb->s_sid) { 300170222Sdougb spx_istat.notme++; 301170222Sdougb goto drop; 302170222Sdougb } 303170222Sdougb spxstat.spxs_connects++; 304170222Sdougb cb->s_did = si->si_sid; 305170222Sdougb cb->s_rack = si->si_ack; 306170222Sdougb cb->s_ralo = si->si_alo; 307170222Sdougb cb->s_dport = ipxp->ipxp_fport = si->si_sport; 308170222Sdougb cb->s_timer[SPXT_REXMT] = 0; 309170222Sdougb cb->s_flags |= SF_ACKNOW; 310170222Sdougb soisconnected(so); 311170222Sdougb cb->s_state = TCPS_ESTABLISHED; 312170222Sdougb /* Use roundtrip time of connection request for initial rtt */ 313170222Sdougb if (cb->s_rtt) { 314170222Sdougb cb->s_srtt = cb->s_rtt << 3; 315170222Sdougb cb->s_rttvar = cb->s_rtt << 1; 316170222Sdougb SPXT_RANGESET(cb->s_rxtcur, 317170222Sdougb ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1, 318170222Sdougb SPXTV_MIN, SPXTV_REXMTMAX); 319170222Sdougb cb->s_rtt = 0; 320170222Sdougb } 321170222Sdougb } 322170222Sdougb if (so->so_options & SO_DEBUG || traceallspxs) 323170222Sdougb spx_trace(SA_INPUT, (u_char)ostate, cb, &spx_savesi, 0); 324170222Sdougb 325170222Sdougb m->m_len -= sizeof(struct ipx); 326170222Sdougb m->m_pkthdr.len -= sizeof(struct ipx); 327170222Sdougb m->m_data += sizeof(struct ipx); 328170222Sdougb 329170222Sdougb if (spx_reass(cb, si)) { 330170222Sdougb m_freem(m); 331170222Sdougb } 332170222Sdougb if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT))) 333170222Sdougb spx_output(cb, (struct mbuf *)NULL); 334170222Sdougb cb->s_flags &= ~(SF_WIN|SF_RXT); 335170222Sdougb return; 336170222Sdougb 337170222Sdougbdropwithreset: 338170222Sdougb if (dropsocket) { 339170222Sdougb struct socket *head; 340170222Sdougb ACCEPT_LOCK(); 341170222Sdougb KASSERT((so->so_qstate & SQ_INCOMP) != 0, 342170222Sdougb ("spx_input: nascent socket not SQ_INCOMP on soabort()")); 343170222Sdougb head = so->so_head; 344170222Sdougb TAILQ_REMOVE(&head->so_incomp, so, so_list); 345170222Sdougb head->so_incqlen--; 346170222Sdougb so->so_qstate &= ~SQ_INCOMP; 347170222Sdougb so->so_head = NULL; 348170222Sdougb ACCEPT_UNLOCK(); 349170222Sdougb soabort(so); 350170222Sdougb } 351186462Sdougb si->si_seq = ntohs(si->si_seq); 352170222Sdougb si->si_ack = ntohs(si->si_ack); 353170222Sdougb si->si_alo = ntohs(si->si_alo); 354170222Sdougb m_freem(dtom(si)); 355186462Sdougb if (cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || traceallspxs) 356170222Sdougb spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0); 357170222Sdougb return; 358170222Sdougb 359170222Sdougbdrop: 360170222Sdougbbad: 361170222Sdougb if (cb == 0 || cb->s_ipxpcb->ipxp_socket->so_options & SO_DEBUG || 362170222Sdougb traceallspxs) 363170222Sdougb spx_trace(SA_DROP, (u_char)ostate, cb, &spx_savesi, 0); 364170222Sdougb m_freem(m); 365170222Sdougb} 366170222Sdougb 367170222Sdougbstatic int spxrexmtthresh = 3; 368170222Sdougb 369170222Sdougb/* 370170222Sdougb * This is structurally similar to the tcp reassembly routine 371170222Sdougb * but its function is somewhat different: It merely queues 372170222Sdougb * packets up, and suppresses duplicates. 373170222Sdougb */ 374170222Sdougbstatic int 375170222Sdougbspx_reass(cb, si) 376170222Sdougbregister struct spxpcb *cb; 377170222Sdougbregister struct spx *si; 378170222Sdougb{ 379170222Sdougb register struct spx_q *q; 380170222Sdougb register struct mbuf *m; 381170222Sdougb register struct socket *so = cb->s_ipxpcb->ipxp_socket; 382170222Sdougb char packetp = cb->s_flags & SF_HI; 383170222Sdougb int incr; 384170222Sdougb char wakeup = 0; 385170222Sdougb 386170222Sdougb if (si == SI(0)) 387170222Sdougb goto present; 388170222Sdougb /* 389170222Sdougb * Update our news from them. 390170222Sdougb */ 391170222Sdougb if (si->si_cc & SPX_SA) 392170222Sdougb cb->s_flags |= (spx_use_delack ? SF_DELACK : SF_ACKNOW); 393170222Sdougb if (SSEQ_GT(si->si_alo, cb->s_ralo)) 394170222Sdougb cb->s_flags |= SF_WIN; 395170222Sdougb if (SSEQ_LEQ(si->si_ack, cb->s_rack)) { 396170222Sdougb if ((si->si_cc & SPX_SP) && cb->s_rack != (cb->s_smax + 1)) { 397170222Sdougb spxstat.spxs_rcvdupack++; 398170222Sdougb /* 399170222Sdougb * If this is a completely duplicate ack 400170222Sdougb * and other conditions hold, we assume 401170222Sdougb * a packet has been dropped and retransmit 402170222Sdougb * it exactly as in tcp_input(). 403170222Sdougb */ 404170222Sdougb if (si->si_ack != cb->s_rack || 405170222Sdougb si->si_alo != cb->s_ralo) 406170222Sdougb cb->s_dupacks = 0; 407170222Sdougb else if (++cb->s_dupacks == spxrexmtthresh) { 408170222Sdougb u_short onxt = cb->s_snxt; 409170222Sdougb int cwnd = cb->s_cwnd; 410170222Sdougb 411170222Sdougb cb->s_snxt = si->si_ack; 412170222Sdougb cb->s_cwnd = CUNIT; 413170222Sdougb cb->s_force = 1 + SPXT_REXMT; 414170222Sdougb spx_output(cb, (struct mbuf *)NULL); 415170222Sdougb cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 416170222Sdougb cb->s_rtt = 0; 417170222Sdougb if (cwnd >= 4 * CUNIT) 418170222Sdougb cb->s_cwnd = cwnd / 2; 419170222Sdougb if (SSEQ_GT(onxt, cb->s_snxt)) 420170222Sdougb cb->s_snxt = onxt; 421170222Sdougb return (1); 422170222Sdougb } 423170222Sdougb } else 424170222Sdougb cb->s_dupacks = 0; 425170222Sdougb goto update_window; 426170222Sdougb } 427170222Sdougb cb->s_dupacks = 0; 428170222Sdougb /* 429170222Sdougb * If our correspondent acknowledges data we haven't sent 430170222Sdougb * TCP would drop the packet after acking. We'll be a little 431170222Sdougb * more permissive 432170222Sdougb */ 433170222Sdougb if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) { 434170222Sdougb spxstat.spxs_rcvacktoomuch++; 435170222Sdougb si->si_ack = cb->s_smax + 1; 436170222Sdougb } 437170222Sdougb spxstat.spxs_rcvackpack++; 438170222Sdougb /* 439170222Sdougb * If transmit timer is running and timed sequence 440170222Sdougb * number was acked, update smoothed round trip time. 441170222Sdougb * See discussion of algorithm in tcp_input.c 442170222Sdougb */ 443170222Sdougb if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) { 444170222Sdougb spxstat.spxs_rttupdated++; 445170222Sdougb if (cb->s_srtt != 0) { 446170222Sdougb register short delta; 447170222Sdougb delta = cb->s_rtt - (cb->s_srtt >> 3); 448170222Sdougb if ((cb->s_srtt += delta) <= 0) 449170222Sdougb cb->s_srtt = 1; 450170222Sdougb if (delta < 0) 451170222Sdougb delta = -delta; 452170222Sdougb delta -= (cb->s_rttvar >> 2); 453170222Sdougb if ((cb->s_rttvar += delta) <= 0) 454170222Sdougb cb->s_rttvar = 1; 455170222Sdougb } else { 456170222Sdougb /* 457170222Sdougb * No rtt measurement yet 458170222Sdougb */ 459170222Sdougb cb->s_srtt = cb->s_rtt << 3; 460170222Sdougb cb->s_rttvar = cb->s_rtt << 1; 461170222Sdougb } 462170222Sdougb cb->s_rtt = 0; 463170222Sdougb cb->s_rxtshift = 0; 464170222Sdougb SPXT_RANGESET(cb->s_rxtcur, 465170222Sdougb ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1, 466170222Sdougb SPXTV_MIN, SPXTV_REXMTMAX); 467170222Sdougb } 468170222Sdougb /* 469170222Sdougb * If all outstanding data is acked, stop retransmit 470170222Sdougb * timer and remember to restart (more output or persist). 471170222Sdougb * If there is more data to be acked, restart retransmit 472170222Sdougb * timer, using current (possibly backed-off) value; 473170222Sdougb */ 474170222Sdougb if (si->si_ack == cb->s_smax + 1) { 475170222Sdougb cb->s_timer[SPXT_REXMT] = 0; 476170222Sdougb cb->s_flags |= SF_RXT; 477170222Sdougb } else if (cb->s_timer[SPXT_PERSIST] == 0) 478170222Sdougb cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 479170222Sdougb /* 480170222Sdougb * When new data is acked, open the congestion window. 481170222Sdougb * If the window gives us less than ssthresh packets 482170222Sdougb * in flight, open exponentially (maxseg at a time). 483170222Sdougb * Otherwise open linearly (maxseg^2 / cwnd at a time). 484170222Sdougb */ 485170222Sdougb incr = CUNIT; 486170222Sdougb if (cb->s_cwnd > cb->s_ssthresh) 487170222Sdougb incr = max(incr * incr / cb->s_cwnd, 1); 488170222Sdougb cb->s_cwnd = min(cb->s_cwnd + incr, cb->s_cwmx); 489170222Sdougb /* 490170222Sdougb * Trim Acked data from output queue. 491170222Sdougb */ 492170222Sdougb while ((m = so->so_snd.sb_mb) != NULL) { 493170222Sdougb if (SSEQ_LT((mtod(m, struct spx *))->si_seq, si->si_ack)) 494170222Sdougb sbdroprecord(&so->so_snd); 495170222Sdougb else 496170222Sdougb break; 497170222Sdougb } 498170222Sdougb sowwakeup(so); 499170222Sdougb cb->s_rack = si->si_ack; 500170222Sdougbupdate_window: 501170222Sdougb if (SSEQ_LT(cb->s_snxt, cb->s_rack)) 502170222Sdougb cb->s_snxt = cb->s_rack; 503170222Sdougb if (SSEQ_LT(cb->s_swl1, si->si_seq) || ((cb->s_swl1 == si->si_seq && 504170222Sdougb (SSEQ_LT(cb->s_swl2, si->si_ack))) || 505170222Sdougb (cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo)))) { 506170222Sdougb /* keep track of pure window updates */ 507170222Sdougb if ((si->si_cc & SPX_SP) && cb->s_swl2 == si->si_ack 508170222Sdougb && SSEQ_LT(cb->s_ralo, si->si_alo)) { 509170222Sdougb spxstat.spxs_rcvwinupd++; 510170222Sdougb spxstat.spxs_rcvdupack--; 511170222Sdougb } 512170222Sdougb cb->s_ralo = si->si_alo; 513170222Sdougb cb->s_swl1 = si->si_seq; 514170222Sdougb cb->s_swl2 = si->si_ack; 515170222Sdougb cb->s_swnd = (1 + si->si_alo - si->si_ack); 516170222Sdougb if (cb->s_swnd > cb->s_smxw) 517186462Sdougb cb->s_smxw = cb->s_swnd; 518170222Sdougb cb->s_flags |= SF_WIN; 519170222Sdougb } 520170222Sdougb /* 521170222Sdougb * If this packet number is higher than that which 522170222Sdougb * we have allocated refuse it, unless urgent 523170222Sdougb */ 524170222Sdougb if (SSEQ_GT(si->si_seq, cb->s_alo)) { 525170222Sdougb if (si->si_cc & SPX_SP) { 526170222Sdougb spxstat.spxs_rcvwinprobe++; 527170222Sdougb return (1); 528170222Sdougb } else 529170222Sdougb spxstat.spxs_rcvpackafterwin++; 530170222Sdougb if (si->si_cc & SPX_OB) { 531170222Sdougb if (SSEQ_GT(si->si_seq, cb->s_alo + 60)) { 532170222Sdougb m_freem(dtom(si)); 533170222Sdougb return (0); 534170222Sdougb } /* else queue this packet; */ 535170222Sdougb } else { 536170222Sdougb /*register struct socket *so = cb->s_ipxpcb->ipxp_socket; 537170222Sdougb if (so->so_state && SS_NOFDREF) { 538170222Sdougb spx_close(cb); 539170222Sdougb } else 540170222Sdougb would crash system*/ 541170222Sdougb spx_istat.notyet++; 542170222Sdougb m_freem(dtom(si)); 543170222Sdougb return (0); 544170222Sdougb } 545170222Sdougb } 546170222Sdougb /* 547170222Sdougb * If this is a system packet, we don't need to 548170222Sdougb * queue it up, and won't update acknowledge # 549170222Sdougb */ 550170222Sdougb if (si->si_cc & SPX_SP) { 551170222Sdougb return (1); 552170222Sdougb } 553170222Sdougb /* 554170222Sdougb * We have already seen this packet, so drop. 555170222Sdougb */ 556170222Sdougb if (SSEQ_LT(si->si_seq, cb->s_ack)) { 557170222Sdougb spx_istat.bdreas++; 558170222Sdougb spxstat.spxs_rcvduppack++; 559170222Sdougb if (si->si_seq == cb->s_ack - 1) 560170222Sdougb spx_istat.lstdup++; 561170222Sdougb return (1); 562186462Sdougb } 563170222Sdougb /* 564170222Sdougb * Loop through all packets queued up to insert in 565170222Sdougb * appropriate sequence. 566170222Sdougb */ 567170222Sdougb for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) { 568170222Sdougb if (si->si_seq == SI(q)->si_seq) { 569170222Sdougb spxstat.spxs_rcvduppack++; 570170222Sdougb return (1); 571170222Sdougb } 572170222Sdougb if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) { 573170222Sdougb spxstat.spxs_rcvoopack++; 574170222Sdougb break; 575170222Sdougb } 576170222Sdougb } 577170222Sdougb insque(si, q->si_prev); 578170222Sdougb /* 579170222Sdougb * If this packet is urgent, inform process 580170222Sdougb */ 581170222Sdougb if (si->si_cc & SPX_OB) { 582170222Sdougb cb->s_iobc = ((char *)si)[1 + sizeof(*si)]; 583170222Sdougb sohasoutofband(so); 584170222Sdougb cb->s_oobflags |= SF_IOOB; 585170222Sdougb } 586170222Sdougbpresent: 587170222Sdougb#define SPINC sizeof(struct spxhdr) 588170222Sdougb /* 589170222Sdougb * Loop through all packets queued up to update acknowledge 590170222Sdougb * number, and present all acknowledged data to user; 591170222Sdougb * If in packet interface mode, show packet headers. 592170222Sdougb */ 593170222Sdougb for (q = cb->s_q.si_next; q != &cb->s_q; q = q->si_next) { 594170222Sdougb if (SI(q)->si_seq == cb->s_ack) { 595170222Sdougb cb->s_ack++; 596170222Sdougb m = dtom(q); 597170222Sdougb if (SI(q)->si_cc & SPX_OB) { 598170222Sdougb cb->s_oobflags &= ~SF_IOOB; 599170222Sdougb SOCKBUF_LOCK(&so->so_rcv); 600170222Sdougb if (so->so_rcv.sb_cc) 601170222Sdougb so->so_oobmark = so->so_rcv.sb_cc; 602170222Sdougb else 603170222Sdougb so->so_rcv.sb_state |= SBS_RCVATMARK; 604170222Sdougb SOCKBUF_UNLOCK(&so->so_rcv); 605170222Sdougb } 606170222Sdougb q = q->si_prev; 607170222Sdougb remque(q->si_next); 608170222Sdougb wakeup = 1; 609170222Sdougb spxstat.spxs_rcvpack++; 610170222Sdougb#ifdef SF_NEWCALL 611170222Sdougb if (cb->s_flags2 & SF_NEWCALL) { 612170222Sdougb struct spxhdr *sp = mtod(m, struct spxhdr *); 613170222Sdougb u_char dt = sp->spx_dt; 614170222Sdougb spx_newchecks[4]++; 615170222Sdougb if (dt != cb->s_rhdr.spx_dt) { 616170222Sdougb struct mbuf *mm = 617170222Sdougb m_getclr(M_DONTWAIT, MT_CONTROL); 618170222Sdougb spx_newchecks[0]++; 619170222Sdougb if (mm != NULL) { 620170222Sdougb u_short *s = 621170222Sdougb mtod(mm, u_short *); 622170222Sdougb cb->s_rhdr.spx_dt = dt; 623170222Sdougb mm->m_len = 5; /*XXX*/ 624170222Sdougb s[0] = 5; 625170222Sdougb s[1] = 1; 626170222Sdougb *(u_char *)(&s[2]) = dt; 627170222Sdougb sbappend(&so->so_rcv, mm); 628170222Sdougb } 629170222Sdougb } 630170222Sdougb if (sp->spx_cc & SPX_OB) { 631170222Sdougb MCHTYPE(m, MT_OOBDATA); 632170222Sdougb spx_newchecks[1]++; 633170222Sdougb SOCKBUF_LOCK(&so->so_rcv); 634170222Sdougb so->so_oobmark = 0; 635170222Sdougb so->so_rcv.sb_state &= ~SBS_RCVATMARK; 636170222Sdougb SOCKBUF_UNLOCK(&so->so_rcv); 637170222Sdougb } 638170222Sdougb if (packetp == 0) { 639170222Sdougb m->m_data += SPINC; 640170222Sdougb m->m_len -= SPINC; 641170222Sdougb m->m_pkthdr.len -= SPINC; 642170222Sdougb } 643170222Sdougb if ((sp->spx_cc & SPX_EM) || packetp) { 644170222Sdougb sbappendrecord(&so->so_rcv, m); 645170222Sdougb spx_newchecks[9]++; 646170222Sdougb } else 647170222Sdougb sbappend(&so->so_rcv, m); 648170222Sdougb } else 649170222Sdougb#endif 650170222Sdougb if (packetp) { 651170222Sdougb sbappendrecord(&so->so_rcv, m); 652170222Sdougb } else { 653170222Sdougb cb->s_rhdr = *mtod(m, struct spxhdr *); 654170222Sdougb m->m_data += SPINC; 655170222Sdougb m->m_len -= SPINC; 656170222Sdougb m->m_pkthdr.len -= SPINC; 657170222Sdougb sbappend(&so->so_rcv, m); 658170222Sdougb } 659170222Sdougb } else 660170222Sdougb break; 661170222Sdougb } 662170222Sdougb if (wakeup) 663170222Sdougb sorwakeup(so); 664170222Sdougb return (0); 665170222Sdougb} 666170222Sdougb 667170222Sdougbvoid 668170222Sdougbspx_ctlinput(cmd, arg_as_sa, dummy) 669170222Sdougb int cmd; 670170222Sdougb struct sockaddr *arg_as_sa; /* XXX should be swapped with dummy */ 671170222Sdougb void *dummy; 672170222Sdougb{ 673170222Sdougb caddr_t arg = (/* XXX */ caddr_t)arg_as_sa; 674170222Sdougb struct ipx_addr *na; 675170222Sdougb struct sockaddr_ipx *sipx; 676170222Sdougb 677170222Sdougb if (cmd < 0 || cmd >= PRC_NCMDS) 678170222Sdougb return; 679170222Sdougb 680170222Sdougb switch (cmd) { 681186462Sdougb 682170222Sdougb case PRC_ROUTEDEAD: 683170222Sdougb return; 684170222Sdougb 685170222Sdougb case PRC_IFDOWN: 686170222Sdougb case PRC_HOSTDEAD: 687170222Sdougb case PRC_HOSTUNREACH: 688170222Sdougb sipx = (struct sockaddr_ipx *)arg; 689170222Sdougb if (sipx->sipx_family != AF_IPX) 690170222Sdougb return; 691170222Sdougb na = &sipx->sipx_addr; 692170222Sdougb break; 693170222Sdougb 694170222Sdougb default: 695170222Sdougb break; 696170222Sdougb } 697170222Sdougb} 698170222Sdougb 699170222Sdougbstatic int 700170222Sdougbspx_output(cb, m0) 701170222Sdougb register struct spxpcb *cb; 702170222Sdougb struct mbuf *m0; 703170222Sdougb{ 704170222Sdougb struct socket *so = cb->s_ipxpcb->ipxp_socket; 705186462Sdougb register struct mbuf *m; 706170222Sdougb register struct spx *si = (struct spx *)NULL; 707170222Sdougb register struct sockbuf *sb = &so->so_snd; 708170222Sdougb int len = 0, win, rcv_win; 709170222Sdougb short span, off, recordp = 0; 710170222Sdougb u_short alo; 711170222Sdougb int error = 0, sendalot; 712170222Sdougb#ifdef notdef 713170222Sdougb int idle; 714170222Sdougb#endif 715170222Sdougb struct mbuf *mprev; 716170222Sdougb 717170222Sdougb if (m0 != NULL) { 718170222Sdougb int mtu = cb->s_mtu; 719170222Sdougb int datalen; 720170222Sdougb /* 721170222Sdougb * Make sure that packet isn't too big. 722170222Sdougb */ 723170222Sdougb for (m = m0; m != NULL; m = m->m_next) { 724170222Sdougb mprev = m; 725170222Sdougb len += m->m_len; 726170222Sdougb if (m->m_flags & M_EOR) 727170222Sdougb recordp = 1; 728170222Sdougb } 729170222Sdougb datalen = (cb->s_flags & SF_HO) ? 730170222Sdougb len - sizeof(struct spxhdr) : len; 731170222Sdougb if (datalen > mtu) { 732170222Sdougb if (cb->s_flags & SF_PI) { 733170222Sdougb m_freem(m0); 734170222Sdougb return (EMSGSIZE); 735170222Sdougb } else { 736170222Sdougb int oldEM = cb->s_cc & SPX_EM; 737170222Sdougb 738170222Sdougb cb->s_cc &= ~SPX_EM; 739170222Sdougb while (len > mtu) { 740170222Sdougb /* 741170222Sdougb * Here we are only being called 742170222Sdougb * from usrreq(), so it is OK to 743170222Sdougb * block. 744170222Sdougb */ 745170222Sdougb m = m_copym(m0, 0, mtu, M_TRYWAIT); 746170222Sdougb if (cb->s_flags & SF_NEWCALL) { 747170222Sdougb struct mbuf *mm = m; 748170222Sdougb spx_newchecks[7]++; 749170222Sdougb while (mm != NULL) { 750170222Sdougb mm->m_flags &= ~M_EOR; 751170222Sdougb mm = mm->m_next; 752170222Sdougb } 753170222Sdougb } 754170222Sdougb error = spx_output(cb, m); 755170222Sdougb if (error) { 756170222Sdougb cb->s_cc |= oldEM; 757170222Sdougb m_freem(m0); 758170222Sdougb return (error); 759170222Sdougb } 760170222Sdougb m_adj(m0, mtu); 761170222Sdougb len -= mtu; 762170222Sdougb } 763170222Sdougb cb->s_cc |= oldEM; 764170222Sdougb } 765170222Sdougb } 766170222Sdougb /* 767170222Sdougb * Force length even, by adding a "garbage byte" if 768170222Sdougb * necessary. 769170222Sdougb */ 770170222Sdougb if (len & 1) { 771170222Sdougb m = mprev; 772170222Sdougb if (M_TRAILINGSPACE(m) >= 1) 773170222Sdougb m->m_len++; 774170222Sdougb else { 775170222Sdougb struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA); 776170222Sdougb 777170222Sdougb if (m1 == NULL) { 778170222Sdougb m_freem(m0); 779170222Sdougb return (ENOBUFS); 780254402Serwin } 781170222Sdougb m1->m_len = 1; 782170222Sdougb *(mtod(m1, u_char *)) = 0; 783254402Serwin m->m_next = m1; 784254402Serwin } 785254402Serwin } 786254402Serwin m = m_gethdr(M_DONTWAIT, MT_HEADER); 787254402Serwin if (m == NULL) { 788170222Sdougb m_freem(m0); 789170222Sdougb return (ENOBUFS); 790170222Sdougb } 791170222Sdougb /* 792170222Sdougb * Fill in mbuf with extended SP header 793170222Sdougb * and addresses and length put into network format. 794170222Sdougb */ 795170222Sdougb MH_ALIGN(m, sizeof(struct spx)); 796170222Sdougb m->m_len = sizeof(struct spx); 797170222Sdougb m->m_next = m0; 798170222Sdougb si = mtod(m, struct spx *); 799170222Sdougb si->si_i = *cb->s_ipx; 800170222Sdougb si->si_s = cb->s_shdr; 801170222Sdougb if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) { 802170222Sdougb register struct spxhdr *sh; 803170222Sdougb if (m0->m_len < sizeof(*sh)) { 804170222Sdougb if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) { 805170222Sdougb m_free(m); 806170222Sdougb m_freem(m0); 807170222Sdougb return (EINVAL); 808170222Sdougb } 809170222Sdougb m->m_next = m0; 810170222Sdougb } 811170222Sdougb sh = mtod(m0, struct spxhdr *); 812170222Sdougb si->si_dt = sh->spx_dt; 813170222Sdougb si->si_cc |= sh->spx_cc & SPX_EM; 814170222Sdougb m0->m_len -= sizeof(*sh); 815170222Sdougb m0->m_data += sizeof(*sh); 816170222Sdougb len -= sizeof(*sh); 817170222Sdougb } 818170222Sdougb len += sizeof(*si); 819170222Sdougb if ((cb->s_flags2 & SF_NEWCALL) && recordp) { 820170222Sdougb si->si_cc |= SPX_EM; 821170222Sdougb spx_newchecks[8]++; 822170222Sdougb } 823170222Sdougb if (cb->s_oobflags & SF_SOOB) { 824170222Sdougb /* 825170222Sdougb * Per jqj@cornell: 826170222Sdougb * make sure OB packets convey exactly 1 byte. 827170222Sdougb * If the packet is 1 byte or larger, we 828170222Sdougb * have already guaranted there to be at least 829170222Sdougb * one garbage byte for the checksum, and 830170222Sdougb * extra bytes shouldn't hurt! 831170222Sdougb */ 832170222Sdougb if (len > sizeof(*si)) { 833254402Serwin si->si_cc |= SPX_OB; 834254402Serwin len = (1 + sizeof(*si)); 835254402Serwin } 836254402Serwin } 837170222Sdougb si->si_len = htons((u_short)len); 838170222Sdougb m->m_pkthdr.len = ((len - 1) | 1) + 1; 839170222Sdougb /* 840170222Sdougb * queue stuff up for output 841170222Sdougb */ 842170222Sdougb sbappendrecord(sb, m); 843170222Sdougb cb->s_seq++; 844170222Sdougb } 845170222Sdougb#ifdef notdef 846170222Sdougb idle = (cb->s_smax == (cb->s_rack - 1)); 847170222Sdougb#endif 848170222Sdougbagain: 849170222Sdougb sendalot = 0; 850170222Sdougb off = cb->s_snxt - cb->s_rack; 851170222Sdougb win = min(cb->s_swnd, (cb->s_cwnd / CUNIT)); 852170222Sdougb 853170222Sdougb /* 854170222Sdougb * If in persist timeout with window of 0, send a probe. 855170222Sdougb * Otherwise, if window is small but nonzero 856170222Sdougb * and timer expired, send what we can and go into 857170222Sdougb * transmit state. 858170222Sdougb */ 859170222Sdougb if (cb->s_force == 1 + SPXT_PERSIST) { 860170222Sdougb if (win != 0) { 861170222Sdougb cb->s_timer[SPXT_PERSIST] = 0; 862170222Sdougb cb->s_rxtshift = 0; 863170222Sdougb } 864170222Sdougb } 865170222Sdougb span = cb->s_seq - cb->s_rack; 866170222Sdougb len = min(span, win) - off; 867170222Sdougb 868170222Sdougb if (len < 0) { 869170222Sdougb /* 870170222Sdougb * Window shrank after we went into it. 871254402Serwin * If window shrank to 0, cancel pending 872254402Serwin * restransmission and pull s_snxt back 873254402Serwin * to (closed) window. We will enter persist 874254402Serwin * state below. If the widndow didn't close completely, 875254402Serwin * just wait for an ACK. 876170222Sdougb */ 877170222Sdougb len = 0; 878170222Sdougb if (win == 0) { 879170222Sdougb cb->s_timer[SPXT_REXMT] = 0; 880170222Sdougb cb->s_snxt = cb->s_rack; 881170222Sdougb } 882170222Sdougb } 883170222Sdougb if (len > 1) 884254402Serwin sendalot = 1; 885170222Sdougb rcv_win = sbspace(&so->so_rcv); 886170222Sdougb 887170222Sdougb /* 888170222Sdougb * Send if we owe peer an ACK. 889170222Sdougb */ 890170222Sdougb if (cb->s_oobflags & SF_SOOB) { 891170222Sdougb /* 892170222Sdougb * must transmit this out of band packet 893170222Sdougb */ 894170222Sdougb cb->s_oobflags &= ~ SF_SOOB; 895170222Sdougb sendalot = 1; 896170222Sdougb spxstat.spxs_sndurg++; 897170222Sdougb goto found; 898170222Sdougb } 899170222Sdougb if (cb->s_flags & SF_ACKNOW) 900170222Sdougb goto send; 901170222Sdougb if (cb->s_state < TCPS_ESTABLISHED) 902170222Sdougb goto send; 903254402Serwin /* 904170222Sdougb * Silly window can't happen in spx. 905170222Sdougb * Code from tcp deleted. 906170222Sdougb */ 907170222Sdougb if (len) 908170222Sdougb goto send; 909170222Sdougb /* 910170222Sdougb * Compare available window to amount of window 911170222Sdougb * known to peer (as advertised window less 912170222Sdougb * next expected input.) If the difference is at least two 913170222Sdougb * packets or at least 35% of the mximum possible window, 914170222Sdougb * then want to send a window update to peer. 915170222Sdougb */ 916170222Sdougb if (rcv_win > 0) { 917170222Sdougb u_short delta = 1 + cb->s_alo - cb->s_ack; 918170222Sdougb int adv = rcv_win - (delta * cb->s_mtu); 919170222Sdougb 920170222Sdougb if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) || 921170222Sdougb (100 * adv / so->so_rcv.sb_hiwat >= 35)) { 922170222Sdougb spxstat.spxs_sndwinup++; 923170222Sdougb cb->s_flags |= SF_ACKNOW; 924170222Sdougb goto send; 925170222Sdougb } 926170222Sdougb 927170222Sdougb } 928186462Sdougb /* 929170222Sdougb * Many comments from tcp_output.c are appropriate here 930170222Sdougb * including . . . 931170222Sdougb * If send window is too small, there is data to transmit, and no 932170222Sdougb * retransmit or persist is pending, then go to persist state. 933170222Sdougb * If nothing happens soon, send when timer expires: 934170222Sdougb * if window is nonzero, transmit what we can, 935170222Sdougb * otherwise send a probe. 936170222Sdougb */ 937170222Sdougb if (so->so_snd.sb_cc && cb->s_timer[SPXT_REXMT] == 0 && 938170222Sdougb cb->s_timer[SPXT_PERSIST] == 0) { 939170222Sdougb cb->s_rxtshift = 0; 940170222Sdougb spx_setpersist(cb); 941170222Sdougb } 942170222Sdougb /* 943170222Sdougb * No reason to send a packet, just return. 944170222Sdougb */ 945170222Sdougb cb->s_outx = 1; 946170222Sdougb return (0); 947170222Sdougb 948170222Sdougbsend: 949170222Sdougb /* 950170222Sdougb * Find requested packet. 951170222Sdougb */ 952170222Sdougb si = 0; 953170222Sdougb if (len > 0) { 954170222Sdougb cb->s_want = cb->s_snxt; 955170222Sdougb for (m = sb->sb_mb; m != NULL; m = m->m_act) { 956170222Sdougb si = mtod(m, struct spx *); 957170222Sdougb if (SSEQ_LEQ(cb->s_snxt, si->si_seq)) 958170222Sdougb break; 959170222Sdougb } 960170222Sdougb found: 961170222Sdougb if (si != NULL) { 962170222Sdougb if (si->si_seq == cb->s_snxt) 963170222Sdougb cb->s_snxt++; 964170222Sdougb else 965170222Sdougb spxstat.spxs_sndvoid++, si = 0; 966170222Sdougb } 967170222Sdougb } 968170222Sdougb /* 969170222Sdougb * update window 970170222Sdougb */ 971170222Sdougb if (rcv_win < 0) 972170222Sdougb rcv_win = 0; 973170222Sdougb alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu)); 974170222Sdougb if (SSEQ_LT(alo, cb->s_alo)) 975170222Sdougb alo = cb->s_alo; 976170222Sdougb 977170222Sdougb if (si != NULL) { 978170222Sdougb /* 979170222Sdougb * must make a copy of this packet for 980186462Sdougb * ipx_output to monkey with 981186462Sdougb */ 982170222Sdougb m = m_copy(dtom(si), 0, (int)M_COPYALL); 983186462Sdougb if (m == NULL) { 984186462Sdougb return (ENOBUFS); 985186462Sdougb } 986186462Sdougb si = mtod(m, struct spx *); 987186462Sdougb if (SSEQ_LT(si->si_seq, cb->s_smax)) 988170222Sdougb spxstat.spxs_sndrexmitpack++; 989170222Sdougb else 990170222Sdougb spxstat.spxs_sndpack++; 991170222Sdougb } else if (cb->s_force || cb->s_flags & SF_ACKNOW) { 992170222Sdougb /* 993170222Sdougb * Must send an acknowledgement or a probe 994170222Sdougb */ 995170222Sdougb if (cb->s_force) 996170222Sdougb spxstat.spxs_sndprobe++; 997170222Sdougb if (cb->s_flags & SF_ACKNOW) 998170222Sdougb spxstat.spxs_sndacks++; 999170222Sdougb m = m_gethdr(M_DONTWAIT, MT_HEADER); 1000170222Sdougb if (m == NULL) 1001170222Sdougb return (ENOBUFS); 1002170222Sdougb /* 1003170222Sdougb * Fill in mbuf with extended SP header 1004170222Sdougb * and addresses and length put into network format. 1005170222Sdougb */ 1006170222Sdougb MH_ALIGN(m, sizeof(struct spx)); 1007170222Sdougb m->m_len = sizeof(*si); 1008170222Sdougb m->m_pkthdr.len = sizeof(*si); 1009170222Sdougb si = mtod(m, struct spx *); 1010170222Sdougb si->si_i = *cb->s_ipx; 1011170222Sdougb si->si_s = cb->s_shdr; 1012170222Sdougb si->si_seq = cb->s_smax + 1; 1013170222Sdougb si->si_len = htons(sizeof(*si)); 1014170222Sdougb si->si_cc |= SPX_SP; 1015170222Sdougb } else { 1016170222Sdougb cb->s_outx = 3; 1017170222Sdougb if (so->so_options & SO_DEBUG || traceallspxs) 1018170222Sdougb spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0); 1019170222Sdougb return (0); 1020170222Sdougb } 1021170222Sdougb /* 1022170222Sdougb * Stuff checksum and output datagram. 1023170222Sdougb */ 1024170222Sdougb if ((si->si_cc & SPX_SP) == 0) { 1025170222Sdougb if (cb->s_force != (1 + SPXT_PERSIST) || 1026170222Sdougb cb->s_timer[SPXT_PERSIST] == 0) { 1027170222Sdougb /* 1028170222Sdougb * If this is a new packet and we are not currently 1029170222Sdougb * timing anything, time this one. 1030170222Sdougb */ 1031170222Sdougb if (SSEQ_LT(cb->s_smax, si->si_seq)) { 1032170222Sdougb cb->s_smax = si->si_seq; 1033170222Sdougb if (cb->s_rtt == 0) { 1034170222Sdougb spxstat.spxs_segstimed++; 1035170222Sdougb cb->s_rtseq = si->si_seq; 1036170222Sdougb cb->s_rtt = 1; 1037170222Sdougb } 1038170222Sdougb } 1039170222Sdougb /* 1040170222Sdougb * Set rexmt timer if not currently set, 1041170222Sdougb * Initial value for retransmit timer is smoothed 1042170222Sdougb * round-trip time + 2 * round-trip time variance. 1043170222Sdougb * Initialize shift counter which is used for backoff 1044170222Sdougb * of retransmit time. 1045170222Sdougb */ 1046170222Sdougb if (cb->s_timer[SPXT_REXMT] == 0 && 1047170222Sdougb cb->s_snxt != cb->s_rack) { 1048170222Sdougb cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 1049170222Sdougb if (cb->s_timer[SPXT_PERSIST]) { 1050170222Sdougb cb->s_timer[SPXT_PERSIST] = 0; 1051170222Sdougb cb->s_rxtshift = 0; 1052170222Sdougb } 1053170222Sdougb } 1054170222Sdougb } else if (SSEQ_LT(cb->s_smax, si->si_seq)) { 1055170222Sdougb cb->s_smax = si->si_seq; 1056170222Sdougb } 1057170222Sdougb } else if (cb->s_state < TCPS_ESTABLISHED) { 1058170222Sdougb if (cb->s_rtt == 0) 1059170222Sdougb cb->s_rtt = 1; /* Time initial handshake */ 1060170222Sdougb if (cb->s_timer[SPXT_REXMT] == 0) 1061170222Sdougb cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 1062170222Sdougb } 1063170222Sdougb { 1064170222Sdougb /* 1065170222Sdougb * Do not request acks when we ack their data packets or 1066170222Sdougb * when we do a gratuitous window update. 1067170222Sdougb */ 1068170222Sdougb if (((si->si_cc & SPX_SP) == 0) || cb->s_force) 1069170222Sdougb si->si_cc |= SPX_SA; 1070170222Sdougb si->si_seq = htons(si->si_seq); 1071170222Sdougb si->si_alo = htons(alo); 1072170222Sdougb si->si_ack = htons(cb->s_ack); 1073170222Sdougb 1074170222Sdougb if (ipxcksum) { 1075170222Sdougb si->si_sum = ipx_cksum(m, ntohs(si->si_len)); 1076170222Sdougb } else 1077170222Sdougb si->si_sum = 0xffff; 1078170222Sdougb 1079170222Sdougb cb->s_outx = 4; 1080170222Sdougb if (so->so_options & SO_DEBUG || traceallspxs) 1081170222Sdougb spx_trace(SA_OUTPUT, cb->s_state, cb, si, 0); 1082170222Sdougb 1083170222Sdougb if (so->so_options & SO_DONTROUTE) 1084170222Sdougb error = ipx_outputfl(m, (struct route *)NULL, IPX_ROUTETOIF); 1085170222Sdougb else 1086170222Sdougb error = ipx_outputfl(m, &cb->s_ipxpcb->ipxp_route, 0); 1087170222Sdougb } 1088170222Sdougb if (error) { 1089170222Sdougb return (error); 1090170222Sdougb } 1091170222Sdougb spxstat.spxs_sndtotal++; 1092170222Sdougb /* 1093170222Sdougb * Data sent (as far as we can tell). 1094170222Sdougb * If this advertises a larger window than any other segment, 1095170222Sdougb * then remember the size of the advertized window. 1096170222Sdougb * Any pending ACK has now been sent. 1097170222Sdougb */ 1098170222Sdougb cb->s_force = 0; 1099170222Sdougb cb->s_flags &= ~(SF_ACKNOW|SF_DELACK); 1100170222Sdougb if (SSEQ_GT(alo, cb->s_alo)) 1101170222Sdougb cb->s_alo = alo; 1102170222Sdougb if (sendalot) 1103170222Sdougb goto again; 1104170222Sdougb cb->s_outx = 5; 1105170222Sdougb return (0); 1106170222Sdougb} 1107170222Sdougb 1108170222Sdougbstatic int spx_do_persist_panics = 0; 1109170222Sdougb 1110170222Sdougbstatic void 1111170222Sdougbspx_setpersist(cb) 1112170222Sdougb register struct spxpcb *cb; 1113170222Sdougb{ 1114170222Sdougb register int t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1; 1115170222Sdougb 1116170222Sdougb if (cb->s_timer[SPXT_REXMT] && spx_do_persist_panics) 1117170222Sdougb panic("spx_output REXMT"); 1118170222Sdougb /* 1119170222Sdougb * Start/restart persistance timer. 1120170222Sdougb */ 1121186462Sdougb SPXT_RANGESET(cb->s_timer[SPXT_PERSIST], 1122170222Sdougb t*spx_backoff[cb->s_rxtshift], 1123170222Sdougb SPXTV_PERSMIN, SPXTV_PERSMAX); 1124170222Sdougb if (cb->s_rxtshift < SPX_MAXRXTSHIFT) 1125170222Sdougb cb->s_rxtshift++; 1126170222Sdougb} 1127170222Sdougb 1128170222Sdougbint 1129170222Sdougbspx_ctloutput(so, sopt) 1130170222Sdougb struct socket *so; 1131170222Sdougb struct sockopt *sopt; 1132170222Sdougb{ 1133170222Sdougb struct ipxpcb *ipxp = sotoipxpcb(so); 1134170222Sdougb register struct spxpcb *cb; 1135170222Sdougb int mask, error; 1136170222Sdougb short soptval; 1137170222Sdougb u_short usoptval; 1138170222Sdougb int optval; 1139170222Sdougb 1140170222Sdougb error = 0; 1141170222Sdougb 1142170222Sdougb if (sopt->sopt_level != IPXPROTO_SPX) { 1143170222Sdougb /* This will have to be changed when we do more general 1144170222Sdougb stacking of protocols */ 1145170222Sdougb return (ipx_ctloutput(so, sopt)); 1146170222Sdougb } 1147170222Sdougb if (ipxp == NULL) 1148170222Sdougb return (EINVAL); 1149170222Sdougb else 1150170222Sdougb cb = ipxtospxpcb(ipxp); 1151170222Sdougb 1152170222Sdougb switch (sopt->sopt_dir) { 1153170222Sdougb case SOPT_GET: 1154170222Sdougb switch (sopt->sopt_name) { 1155170222Sdougb case SO_HEADERS_ON_INPUT: 1156170222Sdougb mask = SF_HI; 1157170222Sdougb goto get_flags; 1158170222Sdougb 1159170222Sdougb case SO_HEADERS_ON_OUTPUT: 1160170222Sdougb mask = SF_HO; 1161170222Sdougb get_flags: 1162170222Sdougb soptval = cb->s_flags & mask; 1163170222Sdougb error = sooptcopyout(sopt, &soptval, sizeof soptval); 1164170222Sdougb break; 1165170222Sdougb 1166170222Sdougb case SO_MTU: 1167170222Sdougb usoptval = cb->s_mtu; 1168170222Sdougb error = sooptcopyout(sopt, &usoptval, sizeof usoptval); 1169170222Sdougb break; 1170170222Sdougb 1171170222Sdougb case SO_LAST_HEADER: 1172170222Sdougb error = sooptcopyout(sopt, &cb->s_rhdr, 1173170222Sdougb sizeof cb->s_rhdr); 1174170222Sdougb break; 1175170222Sdougb 1176170222Sdougb case SO_DEFAULT_HEADERS: 1177170222Sdougb error = sooptcopyout(sopt, &cb->s_shdr, 1178170222Sdougb sizeof cb->s_shdr); 1179170222Sdougb break; 1180170222Sdougb 1181170222Sdougb default: 1182170222Sdougb error = ENOPROTOOPT; 1183170222Sdougb } 1184170222Sdougb break; 1185170222Sdougb 1186170222Sdougb case SOPT_SET: 1187170222Sdougb switch (sopt->sopt_name) { 1188170222Sdougb /* XXX why are these shorts on get and ints on set? 1189170222Sdougb that doesn't make any sense... */ 1190170222Sdougb case SO_HEADERS_ON_INPUT: 1191170222Sdougb mask = SF_HI; 1192170222Sdougb goto set_head; 1193170222Sdougb 1194170222Sdougb case SO_HEADERS_ON_OUTPUT: 1195170222Sdougb mask = SF_HO; 1196186462Sdougb set_head: 1197170222Sdougb error = sooptcopyin(sopt, &optval, sizeof optval, 1198170222Sdougb sizeof optval); 1199170222Sdougb if (error) 1200170222Sdougb break; 1201170222Sdougb 1202170222Sdougb if (cb->s_flags & SF_PI) { 1203170222Sdougb if (optval) 1204170222Sdougb cb->s_flags |= mask; 1205170222Sdougb else 1206170222Sdougb cb->s_flags &= ~mask; 1207170222Sdougb } else error = EINVAL; 1208170222Sdougb break; 1209170222Sdougb 1210170222Sdougb case SO_MTU: 1211170222Sdougb error = sooptcopyin(sopt, &usoptval, sizeof usoptval, 1212170222Sdougb sizeof usoptval); 1213170222Sdougb if (error) 1214170222Sdougb break; 1215170222Sdougb cb->s_mtu = usoptval; 1216170222Sdougb break; 1217170222Sdougb 1218170222Sdougb#ifdef SF_NEWCALL 1219170222Sdougb case SO_NEWCALL: 1220170222Sdougb error = sooptcopyin(sopt, &optval, sizeof optval, 1221170222Sdougb sizeof optval); 1222170222Sdougb if (error) 1223170222Sdougb break; 1224170222Sdougb if (optval) { 1225170222Sdougb cb->s_flags2 |= SF_NEWCALL; 1226170222Sdougb spx_newchecks[5]++; 1227170222Sdougb } else { 1228170222Sdougb cb->s_flags2 &= ~SF_NEWCALL; 1229170222Sdougb spx_newchecks[6]++; 1230170222Sdougb } 1231170222Sdougb break; 1232170222Sdougb#endif 1233170222Sdougb 1234170222Sdougb case SO_DEFAULT_HEADERS: 1235170222Sdougb { 1236170222Sdougb struct spxhdr sp; 1237170222Sdougb 1238170222Sdougb error = sooptcopyin(sopt, &sp, sizeof sp, 1239170222Sdougb sizeof sp); 1240170222Sdougb if (error) 1241170222Sdougb break; 1242170222Sdougb cb->s_dt = sp.spx_dt; 1243170222Sdougb cb->s_cc = sp.spx_cc & SPX_EM; 1244170222Sdougb } 1245170222Sdougb break; 1246170222Sdougb 1247170222Sdougb default: 1248170222Sdougb error = ENOPROTOOPT; 1249170222Sdougb } 1250170222Sdougb break; 1251170222Sdougb } 1252170222Sdougb return (error); 1253170222Sdougb} 1254170222Sdougb 1255170222Sdougbstatic int 1256170222Sdougbspx_usr_abort(so) 1257170222Sdougb struct socket *so; 1258170222Sdougb{ 1259170222Sdougb int s; 1260170222Sdougb struct ipxpcb *ipxp; 1261170222Sdougb struct spxpcb *cb; 1262170222Sdougb 1263170222Sdougb ipxp = sotoipxpcb(so); 1264170222Sdougb cb = ipxtospxpcb(ipxp); 1265170222Sdougb 1266170222Sdougb s = splnet(); 1267170222Sdougb spx_drop(cb, ECONNABORTED); 1268170222Sdougb splx(s); 1269170222Sdougb return (0); 1270170222Sdougb} 1271170222Sdougb 1272170222Sdougb/* 1273170222Sdougb * Accept a connection. Essentially all the work is 1274170222Sdougb * done at higher levels; just return the address 1275170222Sdougb * of the peer, storing through addr. 1276170222Sdougb */ 1277170222Sdougbstatic int 1278170222Sdougbspx_accept(so, nam) 1279170222Sdougb struct socket *so; 1280170222Sdougb struct sockaddr **nam; 1281170222Sdougb{ 1282170222Sdougb struct ipxpcb *ipxp; 1283170222Sdougb struct sockaddr_ipx *sipx, ssipx; 1284170222Sdougb 1285170222Sdougb ipxp = sotoipxpcb(so); 1286170222Sdougb sipx = &ssipx; 1287170222Sdougb bzero(sipx, sizeof *sipx); 1288170222Sdougb sipx->sipx_len = sizeof *sipx; 1289170222Sdougb sipx->sipx_family = AF_IPX; 1290170222Sdougb sipx->sipx_addr = ipxp->ipxp_faddr; 1291170222Sdougb *nam = sodupsockaddr((struct sockaddr *)sipx, M_NOWAIT); 1292170222Sdougb return (0); 1293170222Sdougb} 1294170222Sdougb 1295170222Sdougbstatic int 1296170222Sdougbspx_attach(so, proto, td) 1297170222Sdougb struct socket *so; 1298170222Sdougb int proto; 1299170222Sdougb struct thread *td; 1300170222Sdougb{ 1301170222Sdougb int error; 1302170222Sdougb int s; 1303170222Sdougb struct ipxpcb *ipxp; 1304170222Sdougb struct spxpcb *cb; 1305170222Sdougb struct mbuf *mm; 1306170222Sdougb struct sockbuf *sb; 1307170222Sdougb 1308170222Sdougb ipxp = sotoipxpcb(so); 1309170222Sdougb cb = ipxtospxpcb(ipxp); 1310170222Sdougb 1311170222Sdougb if (ipxp != NULL) 1312170222Sdougb return (EISCONN); 1313170222Sdougb s = splnet(); 1314170222Sdougb error = ipx_pcballoc(so, &ipxpcb_list, td); 1315170222Sdougb if (error) 1316170222Sdougb goto spx_attach_end; 1317170222Sdougb if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 1318170222Sdougb error = soreserve(so, (u_long) 3072, (u_long) 3072); 1319170222Sdougb if (error) 1320170222Sdougb goto spx_attach_end; 1321170222Sdougb } 1322170222Sdougb ipxp = sotoipxpcb(so); 1323170222Sdougb 1324170222Sdougb MALLOC(cb, struct spxpcb *, sizeof *cb, M_PCB, M_NOWAIT | M_ZERO); 1325170222Sdougb 1326170222Sdougb if (cb == NULL) { 1327170222Sdougb error = ENOBUFS; 1328170222Sdougb goto spx_attach_end; 1329170222Sdougb } 1330170222Sdougb sb = &so->so_snd; 1331170222Sdougb 1332170222Sdougb mm = m_getclr(M_DONTWAIT, MT_HEADER); 1333170222Sdougb if (mm == NULL) { 1334170222Sdougb FREE(cb, M_PCB); 1335170222Sdougb error = ENOBUFS; 1336170222Sdougb goto spx_attach_end; 1337170222Sdougb } 1338170222Sdougb cb->s_ipx = mtod(mm, struct ipx *); 1339170222Sdougb cb->s_state = TCPS_LISTEN; 1340170222Sdougb cb->s_smax = -1; 1341170222Sdougb cb->s_swl1 = -1; 1342170222Sdougb cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q; 1343170222Sdougb cb->s_ipxpcb = ipxp; 1344170222Sdougb cb->s_mtu = 576 - sizeof(struct spx); 1345170222Sdougb cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu; 1346170222Sdougb cb->s_ssthresh = cb->s_cwnd; 1347170222Sdougb cb->s_cwmx = sbspace(sb) * CUNIT / (2 * sizeof(struct spx)); 1348170222Sdougb /* Above is recomputed when connecting to account 1349170222Sdougb for changed buffering or mtu's */ 1350170222Sdougb cb->s_rtt = SPXTV_SRTTBASE; 1351170222Sdougb cb->s_rttvar = SPXTV_SRTTDFLT << 2; 1352170222Sdougb SPXT_RANGESET(cb->s_rxtcur, 1353170222Sdougb ((SPXTV_SRTTBASE >> 2) + (SPXTV_SRTTDFLT << 2)) >> 1, 1354170222Sdougb SPXTV_MIN, SPXTV_REXMTMAX); 1355170222Sdougb ipxp->ipxp_pcb = (caddr_t)cb; 1356170222Sdougbspx_attach_end: 1357170222Sdougb splx(s); 1358170222Sdougb return (error); 1359170222Sdougb} 1360170222Sdougb 1361170222Sdougbstatic int 1362170222Sdougbspx_bind(so, nam, td) 1363170222Sdougb struct socket *so; 1364170222Sdougb struct sockaddr *nam; 1365170222Sdougb struct thread *td; 1366170222Sdougb{ 1367170222Sdougb struct ipxpcb *ipxp; 1368170222Sdougb 1369170222Sdougb ipxp = sotoipxpcb(so); 1370170222Sdougb 1371170222Sdougb return (ipx_pcbbind(ipxp, nam, td)); 1372170222Sdougb} 1373170222Sdougb 1374170222Sdougb/* 1375170222Sdougb * Initiate connection to peer. 1376170222Sdougb * Enter SYN_SENT state, and mark socket as connecting. 1377170222Sdougb * Start keep-alive timer, setup prototype header, 1378170222Sdougb * Send initial system packet requesting connection. 1379170222Sdougb */ 1380170222Sdougbstatic int 1381170222Sdougbspx_connect(so, nam, td) 1382170222Sdougb struct socket *so; 1383170222Sdougb struct sockaddr *nam; 1384170222Sdougb struct thread *td; 1385186462Sdougb{ 1386186462Sdougb int error; 1387186462Sdougb int s; 1388170222Sdougb struct ipxpcb *ipxp; 1389170222Sdougb struct spxpcb *cb; 1390170222Sdougb 1391186462Sdougb ipxp = sotoipxpcb(so); 1392186462Sdougb cb = ipxtospxpcb(ipxp); 1393170222Sdougb 1394170222Sdougb s = splnet(); 1395170222Sdougb if (ipxp->ipxp_lport == 0) { 1396170222Sdougb error = ipx_pcbbind(ipxp, (struct sockaddr *)NULL, td); 1397170222Sdougb if (error) 1398170222Sdougb goto spx_connect_end; 1399170222Sdougb } 1400170222Sdougb error = ipx_pcbconnect(ipxp, nam, td); 1401170222Sdougb if (error) 1402170222Sdougb goto spx_connect_end; 1403170222Sdougb soisconnecting(so); 1404170222Sdougb spxstat.spxs_connattempt++; 1405170222Sdougb cb->s_state = TCPS_SYN_SENT; 1406170222Sdougb cb->s_did = 0; 1407170222Sdougb spx_template(cb); 1408170222Sdougb cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 1409170222Sdougb cb->s_force = 1 + SPXTV_KEEP; 1410186462Sdougb /* 1411170222Sdougb * Other party is required to respond to 1412170222Sdougb * the port I send from, but he is not 1413170222Sdougb * required to answer from where I am sending to, 1414170222Sdougb * so allow wildcarding. 1415170222Sdougb * original port I am sending to is still saved in 1416170222Sdougb * cb->s_dport. 1417170222Sdougb */ 1418170222Sdougb ipxp->ipxp_fport = 0; 1419170222Sdougb error = spx_output(cb, (struct mbuf *)NULL); 1420170222Sdougbspx_connect_end: 1421170222Sdougb splx(s); 1422170222Sdougb return (error); 1423170222Sdougb} 1424170222Sdougb 1425170222Sdougbstatic int 1426170222Sdougbspx_detach(so) 1427170222Sdougb struct socket *so; 1428170222Sdougb{ 1429170222Sdougb int s; 1430170222Sdougb struct ipxpcb *ipxp; 1431170222Sdougb struct spxpcb *cb; 1432170222Sdougb 1433170222Sdougb ipxp = sotoipxpcb(so); 1434170222Sdougb cb = ipxtospxpcb(ipxp); 1435170222Sdougb 1436170222Sdougb if (ipxp == NULL) 1437170222Sdougb return (ENOTCONN); 1438170222Sdougb s = splnet(); 1439170222Sdougb if (cb->s_state > TCPS_LISTEN) 1440170222Sdougb spx_disconnect(cb); 1441170222Sdougb else 1442170222Sdougb spx_close(cb); 1443170222Sdougb splx(s); 1444170222Sdougb return (0); 1445170222Sdougb} 1446170222Sdougb 1447170222Sdougb/* 1448170222Sdougb * We may decide later to implement connection closing 1449170222Sdougb * handshaking at the spx level optionally. 1450170222Sdougb * here is the hook to do it: 1451170222Sdougb */ 1452170222Sdougbstatic int 1453170222Sdougbspx_usr_disconnect(so) 1454170222Sdougb struct socket *so; 1455170222Sdougb{ 1456170222Sdougb int s; 1457170222Sdougb struct ipxpcb *ipxp; 1458170222Sdougb struct spxpcb *cb; 1459170222Sdougb 1460170222Sdougb ipxp = sotoipxpcb(so); 1461170222Sdougb cb = ipxtospxpcb(ipxp); 1462170222Sdougb 1463170222Sdougb s = splnet(); 1464170222Sdougb spx_disconnect(cb); 1465170222Sdougb splx(s); 1466170222Sdougb return (0); 1467170222Sdougb} 1468170222Sdougb 1469170222Sdougbstatic int 1470170222Sdougbspx_listen(so, td) 1471170222Sdougb struct socket *so; 1472170222Sdougb struct thread *td; 1473170222Sdougb{ 1474170222Sdougb int error; 1475170222Sdougb struct ipxpcb *ipxp; 1476170222Sdougb struct spxpcb *cb; 1477170222Sdougb 1478170222Sdougb error = 0; 1479170222Sdougb ipxp = sotoipxpcb(so); 1480170222Sdougb cb = ipxtospxpcb(ipxp); 1481170222Sdougb 1482170222Sdougb if (ipxp->ipxp_lport == 0) 1483170222Sdougb error = ipx_pcbbind(ipxp, (struct sockaddr *)NULL, td); 1484170222Sdougb if (error == 0) 1485170222Sdougb cb->s_state = TCPS_LISTEN; 1486170222Sdougb return (error); 1487170222Sdougb} 1488170222Sdougb 1489170222Sdougb/* 1490170222Sdougb * After a receive, possibly send acknowledgment 1491170222Sdougb * updating allocation. 1492170222Sdougb */ 1493170222Sdougbstatic int 1494170222Sdougbspx_rcvd(so, flags) 1495170222Sdougb struct socket *so; 1496170222Sdougb int flags; 1497170222Sdougb{ 1498170222Sdougb int s; 1499170222Sdougb struct ipxpcb *ipxp; 1500170222Sdougb struct spxpcb *cb; 1501170222Sdougb 1502170222Sdougb ipxp = sotoipxpcb(so); 1503170222Sdougb cb = ipxtospxpcb(ipxp); 1504170222Sdougb 1505170222Sdougb s = splnet(); 1506170222Sdougb cb->s_flags |= SF_RVD; 1507170222Sdougb spx_output(cb, (struct mbuf *)NULL); 1508170222Sdougb cb->s_flags &= ~SF_RVD; 1509170222Sdougb splx(s); 1510170222Sdougb return (0); 1511170222Sdougb} 1512170222Sdougb 1513170222Sdougbstatic int 1514170222Sdougbspx_rcvoob(so, m, flags) 1515170222Sdougb struct socket *so; 1516170222Sdougb struct mbuf *m; 1517170222Sdougb int flags; 1518170222Sdougb{ 1519170222Sdougb struct ipxpcb *ipxp; 1520170222Sdougb struct spxpcb *cb; 1521170222Sdougb 1522170222Sdougb ipxp = sotoipxpcb(so); 1523170222Sdougb cb = ipxtospxpcb(ipxp); 1524170222Sdougb 1525170222Sdougb if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark || 1526170222Sdougb (so->so_rcv.sb_state & SBS_RCVATMARK)) { 1527170222Sdougb m->m_len = 1; 1528170222Sdougb *mtod(m, caddr_t) = cb->s_iobc; 1529170222Sdougb return (0); 1530170222Sdougb } 1531170222Sdougb return (EINVAL); 1532170222Sdougb} 1533170222Sdougb 1534170222Sdougbstatic int 1535170222Sdougbspx_send(so, flags, m, addr, controlp, td) 1536170222Sdougb struct socket *so; 1537170222Sdougb int flags; 1538170222Sdougb struct mbuf *m; 1539170222Sdougb struct sockaddr *addr; 1540170222Sdougb struct mbuf *controlp; 1541170222Sdougb struct thread *td; 1542170222Sdougb{ 1543170222Sdougb int error; 1544170222Sdougb int s; 1545170222Sdougb struct ipxpcb *ipxp; 1546170222Sdougb struct spxpcb *cb; 1547170222Sdougb 1548170222Sdougb error = 0; 1549170222Sdougb ipxp = sotoipxpcb(so); 1550170222Sdougb cb = ipxtospxpcb(ipxp); 1551170222Sdougb 1552170222Sdougb s = splnet(); 1553170222Sdougb if (flags & PRUS_OOB) { 1554170222Sdougb if (sbspace(&so->so_snd) < -512) { 1555170222Sdougb error = ENOBUFS; 1556170222Sdougb goto spx_send_end; 1557170222Sdougb } 1558170222Sdougb cb->s_oobflags |= SF_SOOB; 1559170222Sdougb } 1560170222Sdougb if (controlp != NULL) { 1561170222Sdougb u_short *p = mtod(controlp, u_short *); 1562170222Sdougb spx_newchecks[2]++; 1563170222Sdougb if ((p[0] == 5) && (p[1] == 1)) { /* XXXX, for testing */ 1564170222Sdougb cb->s_shdr.spx_dt = *(u_char *)(&p[2]); 1565170222Sdougb spx_newchecks[3]++; 1566170222Sdougb } 1567170222Sdougb m_freem(controlp); 1568170222Sdougb } 1569170222Sdougb controlp = NULL; 1570170222Sdougb error = spx_output(cb, m); 1571170222Sdougb m = NULL; 1572170222Sdougbspx_send_end: 1573170222Sdougb if (controlp != NULL) 1574170222Sdougb m_freem(controlp); 1575170222Sdougb if (m != NULL) 1576170222Sdougb m_freem(m); 1577170222Sdougb splx(s); 1578170222Sdougb return (error); 1579170222Sdougb} 1580170222Sdougb 1581170222Sdougbstatic int 1582170222Sdougbspx_shutdown(so) 1583170222Sdougb struct socket *so; 1584170222Sdougb{ 1585170222Sdougb int error; 1586170222Sdougb int s; 1587170222Sdougb struct ipxpcb *ipxp; 1588170222Sdougb struct spxpcb *cb; 1589170222Sdougb 1590170222Sdougb error = 0; 1591170222Sdougb ipxp = sotoipxpcb(so); 1592170222Sdougb cb = ipxtospxpcb(ipxp); 1593170222Sdougb 1594170222Sdougb s = splnet(); 1595170222Sdougb socantsendmore(so); 1596170222Sdougb cb = spx_usrclosed(cb); 1597170222Sdougb if (cb != NULL) 1598170222Sdougb error = spx_output(cb, (struct mbuf *)NULL); 1599170222Sdougb splx(s); 1600170222Sdougb return (error); 1601170222Sdougb} 1602170222Sdougb 1603170222Sdougbstatic int 1604170222Sdougbspx_sp_attach(so, proto, td) 1605170222Sdougb struct socket *so; 1606170222Sdougb int proto; 1607170222Sdougb struct thread *td; 1608170222Sdougb{ 1609170222Sdougb int error; 1610170222Sdougb struct ipxpcb *ipxp; 1611170222Sdougb 1612170222Sdougb error = spx_attach(so, proto, td); 1613170222Sdougb if (error == 0) { 1614170222Sdougb ipxp = sotoipxpcb(so); 1615170222Sdougb ((struct spxpcb *)ipxp->ipxp_pcb)->s_flags |= 1616170222Sdougb (SF_HI | SF_HO | SF_PI); 1617170222Sdougb } 1618170222Sdougb return (error); 1619170222Sdougb} 1620170222Sdougb 1621170222Sdougb/* 1622170222Sdougb * Create template to be used to send spx packets on a connection. 1623170222Sdougb * Called after host entry created, fills 1624170222Sdougb * in a skeletal spx header (choosing connection id), 1625170222Sdougb * minimizing the amount of work necessary when the connection is used. 1626170222Sdougb */ 1627170222Sdougbstatic void 1628170222Sdougbspx_template(cb) 1629170222Sdougb register struct spxpcb *cb; 1630170222Sdougb{ 1631170222Sdougb register struct ipxpcb *ipxp = cb->s_ipxpcb; 1632170222Sdougb register struct ipx *ipx = cb->s_ipx; 1633170222Sdougb register struct sockbuf *sb = &(ipxp->ipxp_socket->so_snd); 1634170222Sdougb 1635170222Sdougb ipx->ipx_pt = IPXPROTO_SPX; 1636170222Sdougb ipx->ipx_sna = ipxp->ipxp_laddr; 1637170222Sdougb ipx->ipx_dna = ipxp->ipxp_faddr; 1638170222Sdougb cb->s_sid = htons(spx_iss); 1639170222Sdougb spx_iss += SPX_ISSINCR/2; 1640170222Sdougb cb->s_alo = 1; 1641170222Sdougb cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu; 1642170222Sdougb cb->s_ssthresh = cb->s_cwnd; /* Try to expand fast to full complement 1643170222Sdougb of large packets */ 1644170222Sdougb cb->s_cwmx = (sbspace(sb) * CUNIT) / (2 * sizeof(struct spx)); 1645170222Sdougb cb->s_cwmx = max(cb->s_cwmx, cb->s_cwnd); 1646170222Sdougb /* But allow for lots of little packets as well */ 1647170222Sdougb} 1648170222Sdougb 1649170222Sdougb/* 1650170222Sdougb * Close a SPIP control block: 1651170222Sdougb * discard spx control block itself 1652170222Sdougb * discard ipx protocol control block 1653170222Sdougb * wake up any sleepers 1654170222Sdougb */ 1655170222Sdougbstatic struct spxpcb * 1656170222Sdougbspx_close(cb) 1657170222Sdougb register struct spxpcb *cb; 1658170222Sdougb{ 1659170222Sdougb register struct spx_q *s; 1660170222Sdougb struct ipxpcb *ipxp = cb->s_ipxpcb; 1661170222Sdougb struct socket *so = ipxp->ipxp_socket; 1662170222Sdougb register struct mbuf *m; 1663170222Sdougb 1664254402Serwin s = cb->s_q.si_next; 1665170222Sdougb while (s != &(cb->s_q)) { 1666254402Serwin s = s->si_next; 1667254402Serwin m = dtom(s->si_prev); 1668170222Sdougb remque(s->si_prev); 1669170222Sdougb m_freem(m); 1670170222Sdougb } 1671254402Serwin m_free(dtom(cb->s_ipx)); 1672254402Serwin FREE(cb, M_PCB); 1673254402Serwin ipxp->ipxp_pcb = 0; 1674254402Serwin soisdisconnected(so); 1675170222Sdougb ipx_pcbdetach(ipxp); 1676170222Sdougb spxstat.spxs_closed++; 1677170222Sdougb return ((struct spxpcb *)NULL); 1678262706Serwin} 1679262706Serwin 1680170222Sdougb/* 1681170222Sdougb * Someday we may do level 3 handshaking 1682170222Sdougb * to close a connection or send a xerox style error. 1683170222Sdougb * For now, just close. 1684170222Sdougb */ 1685170222Sdougbstatic struct spxpcb * 1686170222Sdougbspx_usrclosed(cb) 1687170222Sdougb register struct spxpcb *cb; 1688170222Sdougb{ 1689170222Sdougb return (spx_close(cb)); 1690170222Sdougb} 1691170222Sdougb 1692170222Sdougbstatic struct spxpcb * 1693170222Sdougbspx_disconnect(cb) 1694170222Sdougb register struct spxpcb *cb; 1695254402Serwin{ 1696254402Serwin return (spx_close(cb)); 1697170222Sdougb} 1698170222Sdougb 1699170222Sdougb/* 1700170222Sdougb * Drop connection, reporting 1701170222Sdougb * the specified error. 1702170222Sdougb */ 1703170222Sdougbstatic struct spxpcb * 1704170222Sdougbspx_drop(cb, errno) 1705170222Sdougb register struct spxpcb *cb; 1706170222Sdougb int errno; 1707170222Sdougb{ 1708170222Sdougb struct socket *so = cb->s_ipxpcb->ipxp_socket; 1709170222Sdougb 1710170222Sdougb /* 1711170222Sdougb * someday, in the xerox world 1712170222Sdougb * we will generate error protocol packets 1713170222Sdougb * announcing that the socket has gone away. 1714170222Sdougb */ 1715170222Sdougb if (TCPS_HAVERCVDSYN(cb->s_state)) { 1716170222Sdougb spxstat.spxs_drops++; 1717170222Sdougb cb->s_state = TCPS_CLOSED; 1718170222Sdougb /*tcp_output(cb);*/ 1719170222Sdougb } else 1720170222Sdougb spxstat.spxs_conndrops++; 1721170222Sdougb so->so_error = errno; 1722170222Sdougb return (spx_close(cb)); 1723170222Sdougb} 1724170222Sdougb 1725170222Sdougb/* 1726170222Sdougb * Fast timeout routine for processing delayed acks 1727170222Sdougb */ 1728170222Sdougbvoid 1729170222Sdougbspx_fasttimo() 1730170222Sdougb{ 1731170222Sdougb register struct ipxpcb *ipxp; 1732170222Sdougb register struct spxpcb *cb; 1733170222Sdougb int s = splnet(); 1734170222Sdougb 1735170222Sdougb LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) { 1736170222Sdougb if ((cb = (struct spxpcb *)ipxp->ipxp_pcb) != NULL && 1737170222Sdougb (cb->s_flags & SF_DELACK)) { 1738170222Sdougb cb->s_flags &= ~SF_DELACK; 1739170222Sdougb cb->s_flags |= SF_ACKNOW; 1740170222Sdougb spxstat.spxs_delack++; 1741170222Sdougb spx_output(cb, (struct mbuf *)NULL); 1742170222Sdougb } 1743170222Sdougb } 1744170222Sdougb 1745170222Sdougb splx(s); 1746170222Sdougb} 1747170222Sdougb 1748170222Sdougb/* 1749170222Sdougb * spx protocol timeout routine called every 500 ms. 1750170222Sdougb * Updates the timers in all active pcb's and 1751170222Sdougb * causes finite state machine actions if timers expire. 1752170222Sdougb */ 1753170222Sdougbvoid 1754170222Sdougbspx_slowtimo() 1755170222Sdougb{ 1756170222Sdougb register struct ipxpcb *ip, *ip_temp; 1757170222Sdougb register struct spxpcb *cb; 1758170222Sdougb int s = splnet(); 1759170222Sdougb register int i; 1760170222Sdougb 1761170222Sdougb /* 1762170222Sdougb * Search through tcb's and update active timers. Note that timers 1763170222Sdougb * may free the ipxpcb, so be sure to handle that case. 1764170222Sdougb */ 1765186462Sdougb LIST_FOREACH_SAFE(ip, &ipxpcb_list, ipxp_list, ip_temp) { 1766170222Sdougb cb = ipxtospxpcb(ip); 1767170222Sdougb if (cb == NULL) 1768170222Sdougb continue; 1769170222Sdougb for (i = 0; i < SPXT_NTIMERS; i++) { 1770170222Sdougb if (cb->s_timer[i] && --cb->s_timer[i] == 0) { 1771170222Sdougb /* 1772170222Sdougb * spx_timers() returns (NULL) if it free'd 1773170222Sdougb * the pcb. 1774170222Sdougb */ 1775170222Sdougb if (spx_timers(cb, i) == NULL) 1776170222Sdougb continue; 1777170222Sdougb } 1778170222Sdougb } 1779170222Sdougb cb->s_idle++; 1780170222Sdougb if (cb->s_rtt) 1781170222Sdougb cb->s_rtt++; 1782170222Sdougb } 1783170222Sdougb spx_iss += SPX_ISSINCR/PR_SLOWHZ; /* increment iss */ 1784170222Sdougb splx(s); 1785254897Serwin} 1786254897Serwin 1787170222Sdougb/* 1788170222Sdougb * SPX timer processing. 1789170222Sdougb */ 1790254402Serwinstatic struct spxpcb * 1791170222Sdougbspx_timers(cb, timer) 1792170222Sdougb register struct spxpcb *cb; 1793170222Sdougb int timer; 1794170222Sdougb{ 1795170222Sdougb long rexmt; 1796254402Serwin int win; 1797170222Sdougb 1798170222Sdougb cb->s_force = 1 + timer; 1799170222Sdougb switch (timer) { 1800170222Sdougb 1801170222Sdougb /* 1802 * 2 MSL timeout in shutdown went off. TCP deletes connection 1803 * control block. 1804 */ 1805 case SPXT_2MSL: 1806 printf("spx: SPXT_2MSL went off for no reason\n"); 1807 cb->s_timer[timer] = 0; 1808 break; 1809 1810 /* 1811 * Retransmission timer went off. Message has not 1812 * been acked within retransmit interval. Back off 1813 * to a longer retransmit interval and retransmit one packet. 1814 */ 1815 case SPXT_REXMT: 1816 if (++cb->s_rxtshift > SPX_MAXRXTSHIFT) { 1817 cb->s_rxtshift = SPX_MAXRXTSHIFT; 1818 spxstat.spxs_timeoutdrop++; 1819 cb = spx_drop(cb, ETIMEDOUT); 1820 break; 1821 } 1822 spxstat.spxs_rexmttimeo++; 1823 rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1; 1824 rexmt *= spx_backoff[cb->s_rxtshift]; 1825 SPXT_RANGESET(cb->s_rxtcur, rexmt, SPXTV_MIN, SPXTV_REXMTMAX); 1826 cb->s_timer[SPXT_REXMT] = cb->s_rxtcur; 1827 /* 1828 * If we have backed off fairly far, our srtt 1829 * estimate is probably bogus. Clobber it 1830 * so we'll take the next rtt measurement as our srtt; 1831 * move the current srtt into rttvar to keep the current 1832 * retransmit times until then. 1833 */ 1834 if (cb->s_rxtshift > SPX_MAXRXTSHIFT / 4 ) { 1835 cb->s_rttvar += (cb->s_srtt >> 2); 1836 cb->s_srtt = 0; 1837 } 1838 cb->s_snxt = cb->s_rack; 1839 /* 1840 * If timing a packet, stop the timer. 1841 */ 1842 cb->s_rtt = 0; 1843 /* 1844 * See very long discussion in tcp_timer.c about congestion 1845 * window and sstrhesh 1846 */ 1847 win = min(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2; 1848 if (win < 2) 1849 win = 2; 1850 cb->s_cwnd = CUNIT; 1851 cb->s_ssthresh = win * CUNIT; 1852 spx_output(cb, (struct mbuf *)NULL); 1853 break; 1854 1855 /* 1856 * Persistance timer into zero window. 1857 * Force a probe to be sent. 1858 */ 1859 case SPXT_PERSIST: 1860 spxstat.spxs_persisttimeo++; 1861 spx_setpersist(cb); 1862 spx_output(cb, (struct mbuf *)NULL); 1863 break; 1864 1865 /* 1866 * Keep-alive timer went off; send something 1867 * or drop connection if idle for too long. 1868 */ 1869 case SPXT_KEEP: 1870 spxstat.spxs_keeptimeo++; 1871 if (cb->s_state < TCPS_ESTABLISHED) 1872 goto dropit; 1873 if (cb->s_ipxpcb->ipxp_socket->so_options & SO_KEEPALIVE) { 1874 if (cb->s_idle >= SPXTV_MAXIDLE) 1875 goto dropit; 1876 spxstat.spxs_keepprobe++; 1877 spx_output(cb, (struct mbuf *)NULL); 1878 } else 1879 cb->s_idle = 0; 1880 cb->s_timer[SPXT_KEEP] = SPXTV_KEEP; 1881 break; 1882 dropit: 1883 spxstat.spxs_keepdrops++; 1884 cb = spx_drop(cb, ETIMEDOUT); 1885 break; 1886 } 1887 return (cb); 1888} 1889