bostic@OKEEFFE.BERKELEY.EDU (Keith Bostic) (04/05/88)
Subject: (netns 2 of 2) updated IP/TCP and XNS sources for 4.3BSD Index: sys 4.3BSD Description: This is number 7 of 11 total articles posted to the newsgroup comp.bugs.4bsd.ucb-fixes. This archive is number 2 of the 2 articles that make up the netns posting. # This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # netns # netns/sp.h # netns/spidp.h # netns/spp_debug.c # netns/spp_debug.h # netns/spp_usrreq.c # netns/spp_var.h # echo c - netns mkdir netns > /dev/null 2>&1 echo x - netns/sp.h sed 's/^X//' >netns/sp.h << 'END-of-netns/sp.h' X/* X * Copyright (c) 1984, 1985, 1986, 1987 Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that this notice is preserved and that due credit is given X * to the University of California at Berkeley. The name of the University X * may not be used to endorse or promote products derived from this X * software without specific prior written permission. This software X * is provided ``as is'' without express or implied warranty. X * X * @(#)sp.h 7.2 (Berkeley) 1/20/88 X */ X X/* X * Definitions for Xerox NS style sequenced packet protocol X */ X Xstruct sphdr { X u_char sp_cc; /* connection control */ X u_char sp_dt; /* datastream type */ X#define SP_SP 0x80 /* system packet */ X#define SP_SA 0x40 /* send acknowledgement */ X#define SP_OB 0x20 /* attention (out of band data) */ X#define SP_EM 0x10 /* end of message */ X u_short sp_sid; /* source connection identifier */ X u_short sp_did; /* destination connection identifier */ X u_short sp_seq; /* sequence number */ X u_short sp_ack; /* acknowledge number */ X u_short sp_alo; /* allocation number */ X}; END-of-netns/sp.h echo x - netns/spidp.h sed 's/^X//' >netns/spidp.h << 'END-of-netns/spidp.h' X/* X * Copyright (c) 1984, 1985, 1986, 1987 Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that this notice is preserved and that due credit is given X * to the University of California at Berkeley. The name of the University X * may not be used to endorse or promote products derived from this X * software without specific prior written permission. This software X * is provided ``as is'' without express or implied warranty. X * X * @(#)spidp.h 7.2 (Berkeley) 1/20/88 X */ X X/* X * Definitions for NS(tm) Internet Datagram Protocol X * containing a Sequenced Packet Protocol packet. X */ Xstruct spidp { X struct idp si_i; X struct sphdr si_s; X}; Xstruct spidp_q { X struct spidp_q *si_next; X struct spidp_q *si_prev; X}; X#define SI(x) ((struct spidp *)x) X#define si_sum si_i.idp_sum X#define si_len si_i.idp_len X#define si_tc si_i.idp_tc X#define si_pt si_i.idp_pt X#define si_dna si_i.idp_dna X#define si_sna si_i.idp_sna X#define si_sport si_i.idp_sna.x_port X#define si_cc si_s.sp_cc X#define si_dt si_s.sp_dt X#define si_sid si_s.sp_sid X#define si_did si_s.sp_did X#define si_seq si_s.sp_seq X#define si_ack si_s.sp_ack X#define si_alo si_s.sp_alo END-of-netns/spidp.h echo x - netns/spp_debug.c sed 's/^X//' >netns/spp_debug.c << 'END-of-netns/spp_debug.c' X/* X * Copyright (c) 1984, 1985, 1986, 1987 Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that this notice is preserved and that due credit is given X * to the University of California at Berkeley. The name of the University X * may not be used to endorse or promote products derived from this X * software without specific prior written permission. This software X * is provided ``as is'' without express or implied warranty. X * X * @(#)spp_debug.c 7.4 (Berkeley) 3/12/88 X */ X X#include "param.h" X#include "systm.h" X#include "mbuf.h" X#include "socket.h" X#include "socketvar.h" X#include "protosw.h" X#include "errno.h" X X#include "../net/route.h" X#include "../net/if.h" X#include "../netinet/tcp_fsm.h" X X#include "ns.h" X#include "ns_pcb.h" X#include "idp.h" X#include "idp_var.h" X#include "sp.h" X#include "spidp.h" X#define SPPTIMERS X#include "spp_timer.h" X#include "spp_var.h" X#define SANAMES X#include "spp_debug.h" X Xint sppconsdebug = 0; X/* X * spp debug routines X */ Xspp_trace(act, ostate, sp, si, req) X short act; X u_char ostate; X struct sppcb *sp; X struct spidp *si; X int req; X{ X#ifdef INET X u_short seq, ack, len, alo; X unsigned long iptime(); X int flags; X struct spp_debug *sd = &spp_debug[spp_debx++]; X extern char *prurequests[]; X extern char *sanames[]; X extern char *tcpstates[]; X extern char *spptimers[]; X X if (spp_debx == SPP_NDEBUG) X spp_debx = 0; X sd->sd_time = iptime(); X sd->sd_act = act; X sd->sd_ostate = ostate; X sd->sd_cb = (caddr_t)sp; X if (sp) X sd->sd_sp = *sp; X else X bzero((caddr_t)&sd->sd_sp, sizeof (*sp)); X if (si) X sd->sd_si = *si; X else X bzero((caddr_t)&sd->sd_si, sizeof (*si)); X sd->sd_req = req; X if (sppconsdebug == 0) X return; X if (ostate >= TCP_NSTATES) ostate = 0; X if (act >= SA_DROP) act = SA_DROP; X if (sp) X printf("%x %s:", sp, tcpstates[ostate]); X else X printf("???????? "); X printf("%s ", sanames[act]); X switch (act) { X X case SA_RESPOND: X case SA_INPUT: X case SA_OUTPUT: X case SA_DROP: X if (si == 0) X break; X seq = si->si_seq; X ack = si->si_ack; X alo = si->si_alo; X len = si->si_len; X if (act == SA_OUTPUT) { X seq = ntohs(seq); X ack = ntohs(ack); X alo = ntohs(alo); X len = ntohs(len); X } X#ifndef lint X#define p1(f) { printf("%s = %x, ", "f", f); } X p1(seq); p1(ack); p1(alo); p1(len); X#endif X flags = si->si_cc; X if (flags) { X char *cp = "<"; X#ifndef lint X#define pf(f) { if (flags&SP_/**/f) { printf("%s%s", cp, "f"); cp = ","; } } X pf(SP); pf(SA); pf(OB); pf(EM); X#else X cp = cp; X#endif X printf(">"); X } X#ifndef lint X#define p2(f) { printf("%s = %x, ", "f", si->si_/**/f); } X p2(sid);p2(did);p2(dt);p2(pt); X#endif X ns_printhost(&si->si_sna); X ns_printhost(&si->si_dna); X X if (act==SA_RESPOND) { X printf("idp_len = %x, ", X ((struct idp *)si)->idp_len); X } X break; X X case SA_USER: X printf("%s", prurequests[req&0xff]); X if ((req & 0xff) == PRU_SLOWTIMO) X printf("<%s>", spptimers[req>>8]); X break; X } X if (sp) X printf(" -> %s", tcpstates[sp->s_state]); X /* print out internal state of sp !?! */ X printf("\n"); X if (sp == 0) X return; X#ifndef lint X#define p3(f) { printf("%s = %x, ", "f", sp->s_/**/f); } X printf("\t"); p3(rack);p3(ralo);p3(smax);p3(flags); printf("\n"); X#endif X#endif X} END-of-netns/spp_debug.c echo x - netns/spp_debug.h sed 's/^X//' >netns/spp_debug.h << 'END-of-netns/spp_debug.h' X/* X * Copyright (c) 1984, 1985, 1986, 1987 Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that this notice is preserved and that due credit is given X * to the University of California at Berkeley. The name of the University X * may not be used to endorse or promote products derived from this X * software without specific prior written permission. This software X * is provided ``as is'' without express or implied warranty. X * X * @(#)spp_debug.h 7.2 (Berkeley) 1/20/88 X */ X Xstruct spp_debug { X u_long sd_time; X short sd_act; X short sd_ostate; X caddr_t sd_cb; X short sd_req; X struct spidp sd_si; X struct sppcb sd_sp; X}; X X#define SA_INPUT 0 X#define SA_OUTPUT 1 X#define SA_USER 2 X#define SA_RESPOND 3 X#define SA_DROP 4 X X#ifdef SANAMES Xchar *sanames[] = X { "input", "output", "user", "respond", "drop" }; X#endif X X#define SPP_NDEBUG 100 Xstruct spp_debug spp_debug[SPP_NDEBUG]; Xint spp_debx; END-of-netns/spp_debug.h echo x - netns/spp_usrreq.c sed 's/^X//' >netns/spp_usrreq.c << 'END-of-netns/spp_usrreq.c' X/* X * Copyright (c) 1984, 1985, 1986, 1987 Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that this notice is preserved and that due credit is given X * to the University of California at Berkeley. The name of the University X * may not be used to endorse or promote products derived from this X * software without specific prior written permission. This software X * is provided ``as is'' without express or implied warranty. X * X * @(#)spp_usrreq.c 7.6 (Berkeley) 3/12/88 X */ X X#include "param.h" X#include "systm.h" X#include "dir.h" X#include "user.h" X#include "mbuf.h" X#include "protosw.h" X#include "socket.h" X#include "socketvar.h" X#include "errno.h" X X#include "../net/if.h" X#include "../net/route.h" X#include "../netinet/tcp_fsm.h" X X#include "ns.h" X#include "ns_pcb.h" X#include "idp.h" X#include "idp_var.h" X#include "ns_error.h" X#include "sp.h" X#include "spidp.h" X#include "spp_timer.h" X#include "spp_var.h" X#include "spp_debug.h" X X/* X * SP protocol implementation. X */ Xspp_init() X{ X X spp_iss = 1; /* WRONG !! should fish it out of TODR */ X} Xstruct spidp spp_savesi; Xint traceallspps = 0; Xextern int sppconsdebug; Xint spp_hardnosed; Xint spp_use_delack = 0; X X/*ARGSUSED*/ Xspp_input(m, nsp, ifp) X register struct mbuf *m; X register struct nspcb *nsp; X struct ifnet *ifp; X{ X register struct sppcb *cb; X register struct spidp *si = mtod(m, struct spidp *); X register struct socket *so; X short ostate; X int dropsocket = 0; X X X sppstat.spps_rcvtotal++; X if (nsp == 0) { X panic("No nspcb in spp_input\n"); X return; X } X X cb = nstosppcb(nsp); X if (cb == 0) goto bad; X X if (m->m_len < sizeof(*si)) { X if ((m = m_pullup(m, sizeof(*si))) == 0) { X sppstat.spps_rcvshort++; X return; X } X si = mtod(m, struct spidp *); X } X si->si_seq = ntohs(si->si_seq); X si->si_ack = ntohs(si->si_ack); X si->si_alo = ntohs(si->si_alo); X X so = nsp->nsp_socket; X if (so->so_options & SO_DEBUG || traceallspps) { X ostate = cb->s_state; X spp_savesi = *si; X } X if (so->so_options & SO_ACCEPTCONN) { X struct sppcb *ocb = cb; X struct socket *oso = so; X so = sonewconn(so); X if (so == 0) { X goto drop; X } X /* X * This is ugly, but .... X * X * Mark socket as temporary until we're X * committed to keeping it. The code at X * ``drop'' and ``dropwithreset'' check the X * flag dropsocket to see if the temporary X * socket created here should be discarded. X * We mark the socket as discardable until X * we're committed to it below in TCPS_LISTEN. X */ X dropsocket++; X nsp = (struct nspcb *)so->so_pcb; X nsp->nsp_laddr = si->si_dna; X cb = nstosppcb(nsp); X cb->s_mtu = ocb->s_mtu; /* preserve sockopts */ X cb->s_flags = ocb->s_flags; /* preserve sockopts */ X if (so->so_snd.sb_hiwat != oso->so_snd.sb_hiwat) /*XXX*/ X sbreserve(&so->so_snd, oso->so_snd.sb_hiwat); X if (so->so_rcv.sb_hiwat != oso->so_rcv.sb_hiwat) /*XXX*/ X sbreserve(&so->so_rcv, oso->so_rcv.sb_hiwat); X cb->s_state = TCPS_LISTEN; X } X X /* X * Packet received on connection. X * reset idle time and keep-alive timer; X */ X cb->s_idle = 0; X cb->s_timer[SPPT_KEEP] = SPPTV_KEEP; X X switch (cb->s_state) { X X case TCPS_LISTEN:{ X struct mbuf *am; X register struct sockaddr_ns *sns; X struct ns_addr laddr; X X /* X * If somebody here was carying on a conversation X * and went away, and his pen pal thinks he can X * still talk, we get the misdirected packet. X */ X if (spp_hardnosed && (si->si_did != 0 || si->si_seq != 0)) { X spp_istat.gonawy++; X goto dropwithreset; X } X am = m_get(M_DONTWAIT, MT_SONAME); X if (am == NULL) X goto drop; X am->m_len = sizeof (struct sockaddr_ns); X sns = mtod(am, struct sockaddr_ns *); X sns->sns_family = AF_NS; X sns->sns_addr = si->si_sna; X laddr = nsp->nsp_laddr; X if (ns_nullhost(laddr)) X nsp->nsp_laddr = si->si_dna; X if (ns_pcbconnect(nsp, am)) { X nsp->nsp_laddr = laddr; X (void) m_free(am); X spp_istat.noconn++; X goto drop; X } X (void) m_free(am); X spp_template(cb); X dropsocket = 0; /* committed to socket */ X cb->s_did = si->si_sid; X cb->s_rack = si->si_ack; X cb->s_ralo = si->si_alo; X#define THREEWAYSHAKE X#ifdef THREEWAYSHAKE X cb->s_state = TCPS_SYN_RECEIVED; X cb->s_force = 1 + SPPT_KEEP; X sppstat.spps_accepts++; X cb->s_timer[SPPT_KEEP] = SPPTV_KEEP; X } X break; X /* X * This state means that we have heard a response X * to our acceptance of their connection X * It is probably logically unnecessary in this X * implementation. X */ X case TCPS_SYN_RECEIVED: { X if (si->si_did!=cb->s_sid) { X spp_istat.wrncon++; X goto drop; X } X#endif X nsp->nsp_fport = si->si_sport; X cb->s_timer[SPPT_REXMT] = 0; X cb->s_timer[SPPT_KEEP] = SPPTV_KEEP; X soisconnected(so); X cb->s_state = TCPS_ESTABLISHED; X sppstat.spps_accepts++; X } X break; X X /* X * This state means that we have gotten a response X * to our attempt to establish a connection. X * We fill in the data from the other side, X * telling us which port to respond to, instead of the well- X * known one we might have sent to in the first place. X * We also require that this is a response to our X * connection id. X */ X case TCPS_SYN_SENT: X if (si->si_did!=cb->s_sid) { X spp_istat.notme++; X goto drop; X } X sppstat.spps_connects++; X cb->s_did = si->si_sid; X cb->s_rack = si->si_ack; X cb->s_ralo = si->si_alo; X cb->s_dport = nsp->nsp_fport = si->si_sport; X cb->s_timer[SPPT_REXMT] = 0; X cb->s_flags |= SF_ACKNOW; X soisconnected(so); X cb->s_state = TCPS_ESTABLISHED; X /* Use roundtrip time of connection request for initial rtt */ X if (cb->s_rtt) { X cb->s_srtt = cb->s_rtt << 3; X cb->s_rttvar = cb->s_rtt << 1; X SPPT_RANGESET(cb->s_rxtcur, X ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1, X SPPTV_MIN, SPPTV_REXMTMAX); X cb->s_rtt = 0; X } X } X if (so->so_options & SO_DEBUG || traceallspps) X spp_trace(SA_INPUT, (u_char)ostate, cb, &spp_savesi, 0); X X m->m_len -= sizeof (struct idp); X m->m_off += sizeof (struct idp); X X if (spp_reass(cb, si)) { X (void) m_freem(m); X } X if (cb->s_force || (cb->s_flags & (SF_ACKNOW|SF_WIN|SF_RXT))) X (void) spp_output(cb, (struct mbuf *)0); X cb->s_flags &= ~(SF_WIN|SF_RXT); X return; X Xdropwithreset: X if (dropsocket) X (void) soabort(so); X si->si_seq = ntohs(si->si_seq); X si->si_ack = ntohs(si->si_ack); X si->si_alo = ntohs(si->si_alo); X ns_error(dtom(si), NS_ERR_NOSOCK, 0); X if (cb->s_nspcb->nsp_socket->so_options & SO_DEBUG || traceallspps) X spp_trace(SA_DROP, (u_char)ostate, cb, &spp_savesi, 0); X return; X Xdrop: Xbad: X if (cb == 0 || cb->s_nspcb->nsp_socket->so_options & SO_DEBUG || X traceallspps) X spp_trace(SA_DROP, (u_char)ostate, cb, &spp_savesi, 0); X m_freem(m); X} X Xint spprexmtthresh = 3; X X/* X * This is structurally similar to the tcp reassembly routine X * but its function is somewhat different: It merely queues X * packets up, and suppresses duplicates. X */ Xspp_reass(cb, si) Xregister struct sppcb *cb; Xregister struct spidp *si; X{ X register struct spidp_q *q; X register struct mbuf *m; X register struct socket *so = cb->s_nspcb->nsp_socket; X char packetp = cb->s_flags & SF_HI; X int incr; X char wakeup = 0; X X if (si == SI(0)) X goto present; X /* X * Update our news from them. X */ X if (si->si_cc & SP_SA) X cb->s_flags |= (spp_use_delack ? SF_DELACK : SF_ACKNOW); X if (SSEQ_GT(si->si_alo, cb->s_ralo)) X cb->s_flags |= SF_WIN; X if (SSEQ_LEQ(si->si_ack, cb->s_rack)) { X if ((si->si_cc & SP_SP) && cb->s_rack != (cb->s_smax + 1)) { X sppstat.spps_rcvdupack++; X /* X * If this is a completely duplicate ack X * and other conditions hold, we assume X * a packet has been dropped and retransmit X * it exactly as in tcp_input(). X */ X if (si->si_ack != cb->s_rack || X si->si_alo != cb->s_ralo) X cb->s_dupacks = 0; X else if (++cb->s_dupacks == spprexmtthresh) { X u_short onxt = cb->s_snxt; X int cwnd = cb->s_cwnd; X X cb->s_snxt = si->si_ack; X cb->s_cwnd = CUNIT; X cb->s_force = 1 + SPPT_REXMT; X (void) spp_output(cb, (struct mbuf *)0); X cb->s_timer[SPPT_REXMT] = cb->s_rxtcur; X cb->s_rtt = 0; X if (cwnd >= 4 * CUNIT) X cb->s_cwnd = cwnd / 2; X if (SSEQ_GT(onxt, cb->s_snxt)) X cb->s_snxt = onxt; X return (1); X } X } else X cb->s_dupacks = 0; X goto update_window; X } X cb->s_dupacks = 0; X /* X * If our correspondent acknowledges data we haven't sent X * TCP would drop the packet after acking. We'll be a little X * more permissive X */ X if (SSEQ_GT(si->si_ack, (cb->s_smax + 1))) { X sppstat.spps_rcvacktoomuch++; X si->si_ack = cb->s_smax + 1; X } X sppstat.spps_rcvackpack++; X /* X * If transmit timer is running and timed sequence X * number was acked, update smoothed round trip time. X * See discussion of algorithm in tcp_input.c X */ X if (cb->s_rtt && SSEQ_GT(si->si_ack, cb->s_rtseq)) { X sppstat.spps_rttupdated++; X if (cb->s_srtt != 0) { X register short delta; X delta = cb->s_rtt - (cb->s_srtt >> 3); X if ((cb->s_srtt += delta) <= 0) X cb->s_srtt = 1; X if (delta < 0) X delta = -delta; X delta -= (cb->s_rttvar >> 2); X if ((cb->s_rttvar += delta) <= 0) X cb->s_rttvar = 1; X } else { X /* X * No rtt measurement yet X */ X cb->s_srtt = cb->s_rtt << 3; X cb->s_rttvar = cb->s_rtt << 1; X } X cb->s_rtt = 0; X cb->s_rxtshift = 0; X SPPT_RANGESET(cb->s_rxtcur, X ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1, X SPPTV_MIN, SPPTV_REXMTMAX); X } X /* X * If all outstanding data is acked, stop retransmit X * timer and remember to restart (more output or persist). X * If there is more data to be acked, restart retransmit X * timer, using current (possibly backed-off) value; X */ X if (si->si_ack == cb->s_smax + 1) { X cb->s_timer[SPPT_REXMT] = 0; X cb->s_flags |= SF_RXT; X } else if (cb->s_timer[SPPT_PERSIST] == 0) X cb->s_timer[SPPT_REXMT] = cb->s_rxtcur; X /* X * When new data is acked, open the congestion window. X * If the window gives us less than ssthresh packets X * in flight, open exponentially (maxseg at a time). X * Otherwise open linearly (maxseg^2 / cwnd at a time). X */ X incr = CUNIT; X if (cb->s_cwnd > cb->s_ssthresh) X incr = MAX(incr * incr / cb->s_cwnd, 1); X cb->s_cwnd = MIN(cb->s_cwnd + incr, cb->s_cwmx); X /* X * Trim Acked data from output queue. X */ X while ((m = so->so_snd.sb_mb) != NULL) { X if (SSEQ_LT((mtod(m, struct spidp *))->si_seq, si->si_ack)) X sbdroprecord(&so->so_snd); X else X break; X } X if ((so->so_snd.sb_flags & SB_WAIT) || so->so_snd.sb_sel) X sowwakeup(so); X cb->s_rack = si->si_ack; Xupdate_window: X if (SSEQ_LT(cb->s_snxt, cb->s_rack)) X cb->s_snxt = cb->s_rack; X if (SSEQ_LT(cb->s_swl1, si->si_seq) || cb->s_swl1 == si->si_seq && X (SSEQ_LT(cb->s_swl2, si->si_ack) || X cb->s_swl2 == si->si_ack && SSEQ_LT(cb->s_ralo, si->si_alo))) { X /* keep track of pure window updates */ X if ((si->si_cc & SP_SP) && cb->s_swl2 == si->si_ack X && SSEQ_LT(cb->s_ralo, si->si_alo)) { X sppstat.spps_rcvwinupd++; X sppstat.spps_rcvdupack--; X } X cb->s_ralo = si->si_alo; X cb->s_swl1 = si->si_seq; X cb->s_swl2 = si->si_ack; X cb->s_swnd = (1 + si->si_alo - si->si_ack); X if (cb->s_swnd > cb->s_smxw) X cb->s_smxw = cb->s_swnd; X cb->s_flags |= SF_WIN; X } X /* X * If this packet number is higher than that which X * we have allocated refuse it, unless urgent X */ X if (SSEQ_GT(si->si_seq, cb->s_alo)) { X if (si->si_cc & SP_SP) { X sppstat.spps_rcvwinprobe++; X return (1); X } else X sppstat.spps_rcvpackafterwin++; X if (si->si_cc & SP_OB) { X if (SSEQ_GT(si->si_seq, cb->s_alo + 60)) { X ns_error(dtom(si), NS_ERR_FULLUP, 0); X return (0); X } /* else queue this packet; */ X } else { X /*register struct socket *so = cb->s_nspcb->nsp_socket; X if (so->so_state && SS_NOFDREF) { X ns_error(dtom(si), NS_ERR_NOSOCK, 0); X (void)spp_close(cb); X } else X would crash system*/ X spp_istat.notyet++; X ns_error(dtom(si), NS_ERR_FULLUP, 0); X return (0); X } X } X /* X * If this is a system packet, we don't need to X * queue it up, and won't update acknowledge # X */ X if (si->si_cc & SP_SP) { X return (1); X } X /* X * We have already seen this packet, so drop. X */ X if (SSEQ_LT(si->si_seq, cb->s_ack)) { X spp_istat.bdreas++; X sppstat.spps_rcvduppack++; X if (si->si_seq == cb->s_ack - 1) X spp_istat.lstdup++; X return (1); X } X /* X * Loop through all packets queued up to insert in X * appropriate sequence. X */ X for (q = cb->s_q.si_next; q!=&cb->s_q; q = q->si_next) { X if (si->si_seq == SI(q)->si_seq) { X sppstat.spps_rcvduppack++; X return (1); X } X if (SSEQ_LT(si->si_seq, SI(q)->si_seq)) { X sppstat.spps_rcvoopack++; X break; X } X } X insque(si, q->si_prev); X /* X * If this packet is urgent, inform process X */ X if (si->si_cc & SP_OB) { X cb->s_iobc = ((char *)si)[1 + sizeof(*si)]; X sohasoutofband(so); X cb->s_oobflags |= SF_IOOB; X } Xpresent: X#define SPINC sizeof(struct sphdr) X /* X * Loop through all packets queued up to update acknowledge X * number, and present all acknowledged data to user; X * If in packet interface mode, show packet headers. X */ X for (q = cb->s_q.si_next; q!=&cb->s_q; q = q->si_next) { X if (SI(q)->si_seq == cb->s_ack) { X cb->s_ack++; X m = dtom(q); X if (SI(q)->si_cc & SP_OB) { X cb->s_oobflags &= ~SF_IOOB; X if (so->so_rcv.sb_cc) X so->so_oobmark = so->so_rcv.sb_cc; X else X so->so_state |= SS_RCVATMARK; X } X q = q->si_prev; X remque(q->si_next); X wakeup = 1; X sppstat.spps_rcvpack++; X if (packetp) { X sbappendrecord(&so->so_rcv, m); X } else { X cb->s_rhdr = *mtod(m, struct sphdr *); X m->m_off += SPINC; X m->m_len -= SPINC; X sbappend(&so->so_rcv, m); X } X } else X break; X } X if (wakeup) sorwakeup(so); X return (0); X} X Xspp_ctlinput(cmd, arg) X int cmd; X caddr_t arg; X{ X struct ns_addr *na; X extern u_char nsctlerrmap[]; X extern spp_abort(), spp_quench(); X extern struct nspcb *idp_drop(); X struct ns_errp *errp; X struct nspcb *nsp; X struct sockaddr_ns *sns; X int type; X X if (cmd < 0 || cmd > PRC_NCMDS) X return; X type = NS_ERR_UNREACH_HOST; X X switch (cmd) { X X case PRC_ROUTEDEAD: X return; X X case PRC_IFDOWN: X case PRC_HOSTDEAD: X case PRC_HOSTUNREACH: X sns = (struct sockaddr_ns *)arg; X if (sns->sns_family != AF_NS) X return; X na = &sns->sns_addr; X break; X X default: X errp = (struct ns_errp *)arg; X na = &errp->ns_err_idp.idp_dna; X type = errp->ns_err_num; X type = ntohs((u_short)type); X } X switch (type) { X X case NS_ERR_UNREACH_HOST: X ns_pcbnotify(na, (int)nsctlerrmap[cmd], spp_abort, (long) 0); X break; X X case NS_ERR_TOO_BIG: X case NS_ERR_NOSOCK: X nsp = ns_pcblookup(na, errp->ns_err_idp.idp_sna.x_port, X NS_WILDCARD); X if (nsp) { X if(nsp->nsp_pcb) X (void) spp_drop((struct sppcb *)nsp->nsp_pcb, X (int)nsctlerrmap[cmd]); X else X (void) idp_drop(nsp, (int)nsctlerrmap[cmd]); X } X break; X X case NS_ERR_FULLUP: X ns_pcbnotify(na, 0, spp_quench, (long) 0); X } X} X/* X * When a source quench is received, close congestion window X * to one packet. We will gradually open it again as we proceed. X */ Xspp_quench(nsp) X struct nspcb *nsp; X{ X struct sppcb *cb = nstosppcb(nsp); X X if (cb) X cb->s_cwnd = CUNIT; X} X X#ifdef notdef Xint Xspp_fixmtu(nsp) Xregister struct nspcb *nsp; X{ X register struct sppcb *cb = (struct sppcb *)(nsp->nsp_pcb); X register struct mbuf *m; X register struct spidp *si; X struct ns_errp *ep; X struct sockbuf *sb; X int badseq, len; X struct mbuf *firstbad, *m0; X X if (cb) { X /* X * The notification that we have sent X * too much is bad news -- we will X * have to go through queued up so far X * splitting ones which are too big and X * reassigning sequence numbers and checksums. X * we should then retransmit all packets from X * one above the offending packet to the last one X * we had sent (or our allocation) X * then the offending one so that the any queued X * data at our destination will be discarded. X */ X ep = (struct ns_errp *)nsp->nsp_notify_param; X sb = &nsp->nsp_socket->so_snd; X cb->s_mtu = ep->ns_err_param; X badseq = SI(&ep->ns_err_idp)->si_seq; X for (m = sb->sb_mb; m; m = m->m_act) { X si = mtod(m, struct spidp *); X if (si->si_seq == badseq) X break; X } X if (m == 0) return; X firstbad = m; X /*for (;;) {*/ X /* calculate length */ X for (m0 = m, len = 0; m ; m = m->m_next) X len += m->m_len; X if (len > cb->s_mtu) { X } X /* FINISH THIS X } */ X } X} X#endif X Xspp_output(cb, m0) X register struct sppcb *cb; X struct mbuf *m0; X{ X struct socket *so = cb->s_nspcb->nsp_socket; X register struct mbuf *m; X register struct spidp *si = (struct spidp *) 0; X register struct sockbuf *sb = &so->so_snd; X int len = 0, win, rcv_win; X short span, off; X u_short alo; X int error = 0, idle, sendalot; X struct mbuf *mprev; X extern int idpcksum; X X if (m0) { X int mtu = cb->s_mtu; X int datalen; X /* X * Make sure that packet isn't too big. X */ X for (m = m0; m ; m = m->m_next) { X mprev = m; X len += m->m_len; X } X datalen = (cb->s_flags & SF_HO) ? X len - sizeof (struct sphdr) : len; X if (datalen > mtu) { X if (cb->s_flags & SF_PI) { X m_freem(m0); X return (EMSGSIZE); X } else { X int oldEM = cb->s_cc & SP_EM; X X cb->s_cc &= ~SP_EM; X while (len > mtu) { X m = m_copy(m0, 0, mtu); X if (m == NULL) { X error = ENOBUFS; X goto bad_copy; X } X error = spp_output(cb, m); X if (error) { X bad_copy: X cb->s_cc |= oldEM; X m_freem(m0); X return(error); X } X m_adj(m0, mtu); X len -= mtu; X } X cb->s_cc |= oldEM; X } X } X /* X * Force length even, by adding a "garbage byte" if X * necessary. X */ X if (len & 1) { X m = mprev; X if (m->m_len + m->m_off < MMAXOFF) X m->m_len++; X else { X struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA); X X if (m1 == 0) { X m_freem(m0); X return (ENOBUFS); X } X m1->m_len = 1; X m1->m_off = MMAXOFF - 1; X m->m_next = m1; X } X } X m = m_get(M_DONTWAIT, MT_HEADER); X if (m == 0) { X m_freem(m0); X return (ENOBUFS); X } X /* X * Fill in mbuf with extended SP header X * and addresses and length put into network format. X * Long align so prepended ip headers will work on Gould. X */ X m->m_off = MMAXOFF - sizeof (struct spidp) - 2; X m->m_len = sizeof (struct spidp); X m->m_next = m0; X si = mtod(m, struct spidp *); X si->si_i = *cb->s_idp; X si->si_s = cb->s_shdr; X if ((cb->s_flags & SF_PI) && (cb->s_flags & SF_HO)) { X register struct sphdr *sh; X if (m0->m_len < sizeof (*sh)) { X if((m0 = m_pullup(m0, sizeof(*sh))) == NULL) { X (void) m_free(m); X m_freem(m0); X return (EINVAL); X } X m->m_next = m0; X } X sh = mtod(m0, struct sphdr *); X si->si_dt = sh->sp_dt; X si->si_cc |= sh->sp_cc & SP_EM; X m0->m_len -= sizeof (*sh); X m0->m_off += sizeof (*sh); X len -= sizeof (*sh); X } X len += sizeof(*si); X if (cb->s_oobflags & SF_SOOB) { X /* X * Per jqj@cornell: X * make sure OB packets convey exactly 1 byte. X * If the packet is 1 byte or larger, we X * have already guaranted there to be at least X * one garbage byte for the checksum, and X * extra bytes shouldn't hurt! X */ X if (len > sizeof(*si)) { X si->si_cc |= SP_OB; X len = (1 + sizeof(*si)); X } X } X si->si_len = htons((u_short)len); X /* X * queue stuff up for output X */ X sbappendrecord(sb, m); X cb->s_seq++; X } X idle = (cb->s_smax == (cb->s_rack - 1)); Xagain: X sendalot = 0; X off = cb->s_snxt - cb->s_rack; X win = MIN(cb->s_swnd, (cb->s_cwnd/CUNIT)); X X /* X * If in persist timeout with window of 0, send a probe. X * Otherwise, if window is small but nonzero X * and timer expired, send what we can and go into X * transmit state. X */ X if (cb->s_force == 1 + SPPT_PERSIST) { X if (win != 0) { X cb->s_timer[SPPT_PERSIST] = 0; X cb->s_rxtshift = 0; X } X } X span = cb->s_seq - cb->s_rack; X len = MIN(span, win) - off; X X if (len < 0) { X /* X * Window shrank after we went into it. X * If window shrank to 0, cancel pending X * restransmission and pull s_snxt back X * to (closed) window. We will enter persist X * state below. If the widndow didn't close completely, X * just wait for an ACK. X */ X len = 0; X if (win == 0) { X cb->s_timer[SPPT_REXMT] = 0; X cb->s_snxt = cb->s_rack; X } X } X if (len > 1) X sendalot = 1; X rcv_win = sbspace(&so->so_rcv); X X /* X * Send if we owe peer an ACK. X */ X if (cb->s_oobflags & SF_SOOB) { X /* X * must transmit this out of band packet X */ X cb->s_oobflags &= ~ SF_SOOB; X sendalot = 1; X sppstat.spps_sndurg++; X goto found; X } X if (cb->s_flags & SF_ACKNOW) X goto send; X if (cb->s_state < TCPS_ESTABLISHED) X goto send; X /* X * Silly window can't happen in spp. X * Code from tcp deleted. X */ X if (len) X goto send; X /* X * Compare available window to amount of window X * known to peer (as advertised window less X * next expected input.) If the difference is at least two X * packets or at least 35% of the mximum possible window, X * then want to send a window update to peer. X */ X if (rcv_win > 0) { X u_short delta = 1 + cb->s_alo - cb->s_ack; X int adv = rcv_win - (delta * cb->s_mtu); X X if ((so->so_rcv.sb_cc == 0 && adv >= (2 * cb->s_mtu)) || X (100 * adv / so->so_rcv.sb_hiwat >= 35)) { X sppstat.spps_sndwinup++; X cb->s_flags |= SF_ACKNOW; X goto send; X } X X } X /* X * Many comments from tcp_output.c are appropriate here X * including . . . X * If send window is too small, there is data to transmit, and no X * retransmit or persist is pending, then go to persist state. X * If nothing happens soon, send when timer expires: X * if window is nonzero, transmit what we can, X * otherwise send a probe. X */ X if (so->so_snd.sb_cc && cb->s_timer[SPPT_REXMT] == 0 && X cb->s_timer[SPPT_PERSIST] == 0) { X cb->s_rxtshift = 0; X spp_setpersist(cb); X } X /* X * No reason to send a packet, just return. X */ X cb->s_outx = 1; X return (0); X Xsend: X /* X * Find requested packet. X */ X si = 0; X if (len > 0) { X cb->s_want = cb->s_snxt; X for (m = sb->sb_mb; m; m = m->m_act) { X si = mtod(m, struct spidp *); X if (SSEQ_LEQ(cb->s_snxt, si->si_seq)) X break; X } X found: X if (si) { X if (si->si_seq == cb->s_snxt) X cb->s_snxt++; X else X sppstat.spps_sndvoid++, si = 0; X } X } X /* X * update window X */ X if (rcv_win < 0) X rcv_win = 0; X alo = cb->s_ack - 1 + (rcv_win / ((short)cb->s_mtu)); X if (SSEQ_LT(alo, cb->s_alo)) X alo = cb->s_alo; X X if (si) { X /* X * must make a copy of this packet for X * idp_output to monkey with X */ X m = m_copy(dtom(si), 0, (int)M_COPYALL); X if (m == NULL) { X return (ENOBUFS); X } X m0 = m; X si = mtod(m, struct spidp *); X if (SSEQ_LT(si->si_seq, cb->s_smax)) X sppstat.spps_sndrexmitpack++; X else X sppstat.spps_sndpack++; X } else if (cb->s_force || cb->s_flags & SF_ACKNOW) { X /* X * Must send an acknowledgement or a probe X */ X if (cb->s_force) X sppstat.spps_sndprobe++; X if (cb->s_flags & SF_ACKNOW) X sppstat.spps_sndacks++; X m = m_get(M_DONTWAIT, MT_HEADER); X if (m == 0) { X return (ENOBUFS); X } X /* X * Fill in mbuf with extended SP header X * and addresses and length put into network format. X * Allign beginning of packet to long to prepend X * ifp's on loopback, or NSIP encaspulation for fussy cpu's. X */ X m->m_off = MMAXOFF - sizeof (struct spidp) - 2; X m->m_len = sizeof (*si); X m->m_next = 0; X si = mtod(m, struct spidp *); X si->si_i = *cb->s_idp; X si->si_s = cb->s_shdr; X si->si_seq = cb->s_smax + 1; X si->si_len = htons(sizeof (*si)); X si->si_cc |= SP_SP; X } else { X cb->s_outx = 3; X if (so->so_options & SO_DEBUG || traceallspps) X spp_trace(SA_OUTPUT, cb->s_state, cb, si, 0); X return (0); X } X /* X * Stuff checksum and output datagram. X */ X if ((si->si_cc & SP_SP) == 0) { X if (cb->s_force != (1 + SPPT_PERSIST) || X cb->s_timer[SPPT_PERSIST] == 0) { X /* X * If this is a new packet and we are not currently X * timing anything, time this one. X */ X if (SSEQ_LT(cb->s_smax, si->si_seq)) { X cb->s_smax = si->si_seq; X if (cb->s_rtt == 0) { X sppstat.spps_segstimed++; X cb->s_rtseq = si->si_seq; X cb->s_rtt = 1; X } X } X /* X * Set rexmt timer if not currently set, X * Initial value for retransmit timer is smoothed X * round-trip time + 2 * round-trip time variance. X * Initialize shift counter which is used for backoff X * of retransmit time. X */ X if (cb->s_timer[SPPT_REXMT] == 0 && X cb->s_snxt != cb->s_rack) { X cb->s_timer[SPPT_REXMT] = cb->s_rxtcur; X if (cb->s_timer[SPPT_PERSIST]) { X cb->s_timer[SPPT_PERSIST] = 0; X cb->s_rxtshift = 0; X } X } X } else if (SSEQ_LT(cb->s_smax, si->si_seq)) { X cb->s_smax = si->si_seq; X } X } else if (cb->s_state < TCPS_ESTABLISHED) { X if (cb->s_rtt == 0) X cb->s_rtt = 1; /* Time initial handshake */ X if (cb->s_timer[SPPT_REXMT] == 0) X cb->s_timer[SPPT_REXMT] = cb->s_rxtcur; X } X { X /* X * Do not request acks when we ack their data packets or X * when we do a gratuitous window update. X */ X if (((si->si_cc & SP_SP) == 0) || cb->s_force) X si->si_cc |= SP_SA; X si->si_seq = htons(si->si_seq); X si->si_alo = htons(alo); X si->si_ack = htons(cb->s_ack); X X if (idpcksum) { X si->si_sum = 0; X len = ntohs(si->si_len); X if (len & 1) X len++; X si->si_sum = ns_cksum(dtom(si), len); X } else X si->si_sum = 0xffff; X X cb->s_outx = 4; X if (so->so_options & SO_DEBUG || traceallspps) X spp_trace(SA_OUTPUT, cb->s_state, cb, si, 0); X X if (so->so_options & SO_DONTROUTE) X error = ns_output(m, (struct route *)0, NS_ROUTETOIF); X else X error = ns_output(m, &cb->s_nspcb->nsp_route, 0); X } X if (error) { X return (error); X } X sppstat.spps_sndtotal++; X /* X * Data sent (as far as we can tell). X * If this advertises a larger window than any other segment, X * then remember the size of the advertized window. X * Any pending ACK has now been sent. X */ X cb->s_force = 0; X cb->s_flags &= ~(SF_ACKNOW|SF_DELACK); X if (SSEQ_GT(alo, cb->s_alo)) X cb->s_alo = alo; X if (sendalot) X goto again; X cb->s_outx = 5; X return (0); X} X Xint spp_do_persist_panics = 0; X Xspp_setpersist(cb) X register struct sppcb *cb; X{ X register t = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1; X extern int spp_backoff[]; X X if (cb->s_timer[SPPT_REXMT] && spp_do_persist_panics) X panic("spp_output REXMT"); X /* X * Start/restart persistance timer. X */ X SPPT_RANGESET(cb->s_timer[SPPT_PERSIST], X t*spp_backoff[cb->s_rxtshift], X SPPTV_PERSMIN, SPPTV_PERSMAX); X if (cb->s_rxtshift < SPP_MAXRXTSHIFT) X cb->s_rxtshift++; X} X/*ARGSUSED*/ Xspp_ctloutput(req, so, level, name, value) X int req; X struct socket *so; X int name; X struct mbuf **value; X{ X register struct mbuf *m; X struct nspcb *nsp = sotonspcb(so); X register struct sppcb *cb; X int mask, error = 0; X X if (level != NSPROTO_SPP) { X /* This will have to be changed when we do more general X stacking of protocols */ X return (idp_ctloutput(req, so, level, name, value)); X } X if (nsp == NULL) { X error = EINVAL; X goto release; X } else X cb = nstosppcb(nsp); X X switch (req) { X X case PRCO_GETOPT: X if (value == NULL) X return (EINVAL); X m = m_get(M_DONTWAIT, MT_DATA); X if (m == NULL) X return (ENOBUFS); X switch (name) { X X case SO_HEADERS_ON_INPUT: X mask = SF_HI; X goto get_flags; X X case SO_HEADERS_ON_OUTPUT: X mask = SF_HO; X get_flags: X m->m_len = sizeof(short); X m->m_off = MMAXOFF - sizeof(short); X *mtod(m, short *) = cb->s_flags & mask; X break; X X case SO_MTU: X m->m_len = sizeof(u_short); X m->m_off = MMAXOFF - sizeof(short); X *mtod(m, short *) = cb->s_mtu; X break; X X case SO_LAST_HEADER: X m->m_len = sizeof(struct sphdr); X m->m_off = MMAXOFF - sizeof(struct sphdr); X *mtod(m, struct sphdr *) = cb->s_rhdr; X break; X X case SO_DEFAULT_HEADERS: X m->m_len = sizeof(struct spidp); X m->m_off = MMAXOFF - sizeof(struct sphdr); X *mtod(m, struct sphdr *) = cb->s_shdr; X break; X X default: X error = EINVAL; X } X *value = m; X break; X X case PRCO_SETOPT: X if (value == 0 || *value == 0) { X error = EINVAL; X break; X } X switch (name) { X int *ok; X X case SO_HEADERS_ON_INPUT: X mask = SF_HI; X goto set_head; X X case SO_HEADERS_ON_OUTPUT: X mask = SF_HO; X set_head: X if (cb->s_flags & SF_PI) { X ok = mtod(*value, int *); X if (*ok) X cb->s_flags |= mask; X else X cb->s_flags &= ~mask; X } else error = EINVAL; X break; X X case SO_MTU: X cb->s_mtu = *(mtod(*value, u_short *)); X break; X X case SO_DEFAULT_HEADERS: X { X register struct sphdr *sp X = mtod(*value, struct sphdr *); X cb->s_dt = sp->sp_dt; X cb->s_cc = sp->sp_cc & SP_EM; X } X break; X X default: X error = EINVAL; X } X m_freem(*value); X break; X } X release: X return (error); X} X X/*ARGSUSED*/ Xspp_usrreq(so, req, m, nam, rights) X struct socket *so; X int req; X struct mbuf *m, *nam, *rights; X{ X struct nspcb *nsp = sotonspcb(so); X register struct sppcb *cb; X int s = splnet(); X int error = 0, ostate; X struct mbuf *mm; X register struct sockbuf *sb; X X if (req == PRU_CONTROL) X return (ns_control(so, (int)m, (caddr_t)nam, X (struct ifnet *)rights)); X if (rights && rights->m_len) { X error = EINVAL; X goto release; X } X if (nsp == NULL) { X if (req != PRU_ATTACH) { X error = EINVAL; X goto release; X } X } else X cb = nstosppcb(nsp); X X ostate = cb ? cb->s_state : 0; X X switch (req) { X X case PRU_ATTACH: X if (nsp != NULL) { X error = EISCONN; X break; X } X error = ns_pcballoc(so, &nspcb); X if (error) X break; X error = soreserve(so, 3072, 3072); X if (error) X break; X nsp = sotonspcb(so); X X mm = m_getclr(M_DONTWAIT, MT_PCB); X sb = &so->so_snd; X X if (mm == NULL) { X error = ENOBUFS; X break; X } X cb = mtod(mm, struct sppcb *); X mm = m_getclr(M_DONTWAIT, MT_HEADER); X if (mm == NULL) { X m_free(dtom(m)); X error = ENOBUFS; X break; X } X cb->s_idp = mtod(mm, struct idp *); X cb->s_state = TCPS_LISTEN; X cb->s_smax = -1; X cb->s_swl1 = -1; X cb->s_q.si_next = cb->s_q.si_prev = &cb->s_q; X cb->s_nspcb = nsp; X cb->s_mtu = 576 - sizeof (struct spidp); X cb->s_cwnd = sbspace(sb) * CUNIT / cb->s_mtu; X cb->s_ssthresh = cb->s_cwnd; X cb->s_cwmx = sb->sb_mbmax * CUNIT / X (2 * sizeof (struct spidp)); X /* Above is recomputed when connecting to account X for changed buffering or mtu's */ X cb->s_rtt = SPPTV_SRTTBASE; X cb->s_rttvar = SPPTV_SRTTDFLT << 2; X SPPT_RANGESET(cb->s_rxtcur, X ((SPPTV_SRTTBASE >> 2) + (SPPTV_SRTTDFLT << 2)) >> 1, X SPPTV_MIN, SPPTV_REXMTMAX); X nsp->nsp_pcb = (caddr_t) cb; X break; X X case PRU_DETACH: X if (nsp == NULL) { X error = ENOTCONN; X break; X } X if (cb->s_state > TCPS_LISTEN) X cb = spp_disconnect(cb); X else X cb = spp_close(cb); X break; X X case PRU_BIND: X error = ns_pcbbind(nsp, nam); X break; X X case PRU_LISTEN: X if (nsp->nsp_lport == 0) X error = ns_pcbbind(nsp, (struct mbuf *)0); X if (error == 0) X cb->s_state = TCPS_LISTEN; X break; X X /* X * Initiate connection to peer. X * Enter SYN_SENT state, and mark socket as connecting. X * Start keep-alive timer, setup prototype header, X * Send initial system packet requesting connection. X */ X case PRU_CONNECT: X if (nsp->nsp_lport == 0) { X error = ns_pcbbind(nsp, (struct mbuf *)0); X if (error) X break; X } X error = ns_pcbconnect(nsp, nam); X if (error) X break; X soisconnecting(so); X sppstat.spps_connattempt++; X cb->s_state = TCPS_SYN_SENT; X cb->s_did = 0; X spp_template(cb); X cb->s_timer[SPPT_KEEP] = SPPTV_KEEP; X cb->s_force = 1 + SPPTV_KEEP; X /* X * Other party is required to respond to X * the port I send from, but he is not X * required to answer from where I am sending to, X * so allow wildcarding. X * original port I am sending to is still saved in X * cb->s_dport. X */ X nsp->nsp_fport = 0; X error = spp_output(cb, (struct mbuf *) 0); X break; X X case PRU_CONNECT2: X error = EOPNOTSUPP; X break; X X /* X * We may decide later to implement connection closing X * handshaking at the spp level optionally. X * here is the hook to do it: X */ X case PRU_DISCONNECT: X cb = spp_disconnect(cb); X break; X X /* X * Accept a connection. Essentially all the work is X * done at higher levels; just return the address X * of the peer, storing through addr. X */ X case PRU_ACCEPT: { X struct sockaddr_ns *sns = mtod(nam, struct sockaddr_ns *); X X nam->m_len = sizeof (struct sockaddr_ns); X sns->sns_family = AF_NS; X sns->sns_addr = nsp->nsp_faddr; X break; X } X X case PRU_SHUTDOWN: X socantsendmore(so); X cb = spp_usrclosed(cb); X if (cb) X error = spp_output(cb, (struct mbuf *) 0); X break; X X /* X * After a receive, possibly send acknowledgment X * updating allocation. X */ X case PRU_RCVD: X cb->s_flags |= SF_RVD; X (void) spp_output(cb, (struct mbuf *) 0); X cb->s_flags &= ~SF_RVD; X break; X X case PRU_ABORT: X (void) spp_drop(cb, ECONNABORTED); X break; X X case PRU_SENSE: X case PRU_CONTROL: X m = NULL; X error = EOPNOTSUPP; X break; X X case PRU_RCVOOB: X if ((cb->s_oobflags & SF_IOOB) || so->so_oobmark || X (so->so_state & SS_RCVATMARK)) { X m->m_len = 1; X *mtod(m, caddr_t) = cb->s_iobc; X break; X } X error = EINVAL; X break; X X case PRU_SENDOOB: X if (sbspace(&so->so_snd) < -512) { X error = ENOBUFS; X break; X } X cb->s_oobflags |= SF_SOOB; X /* fall into */ X case PRU_SEND: X error = spp_output(cb, m); X m = NULL; X break; X X case PRU_SOCKADDR: X ns_setsockaddr(nsp, nam); X break; X X case PRU_PEERADDR: X ns_setpeeraddr(nsp, nam); X break; X X case PRU_SLOWTIMO: X cb = spp_timers(cb, (int)nam); X req |= ((int)nam) << 8; X break; X X case PRU_FASTTIMO: X case PRU_PROTORCV: X case PRU_PROTOSEND: X error = EOPNOTSUPP; X break; X X default: X panic("sp_usrreq"); X } X if (cb && (so->so_options & SO_DEBUG || traceallspps)) X spp_trace(SA_USER, (u_char)ostate, cb, (struct spidp *)0, req); Xrelease: X if (m != NULL) X m_freem(m); X splx(s); X return (error); X} X Xspp_usrreq_sp(so, req, m, nam, rights) X struct socket *so; X int req; X struct mbuf *m, *nam, *rights; X{ X int error = spp_usrreq(so, req, m, nam, rights); X X if (req == PRU_ATTACH && error == 0) { X struct nspcb *nsp = sotonspcb(so); X ((struct sppcb *)nsp->nsp_pcb)->s_flags |= X (SF_HI | SF_HO | SF_PI); X } X return (error); X} X X/* X * Create template to be used to send spp packets on a connection. X * Called after host entry created, fills X * in a skeletal spp header (choosing connection id), X * minimizing the amount of work necessary when the connection is used. X */ Xspp_template(cb) X register struct sppcb *cb; X{ X register struct nspcb *nsp = cb->s_nspcb; X register struct idp *idp = cb->s_idp; X register struct sockbuf *sb = &(nsp->nsp_socket->so_snd); X X idp->idp_pt = NSPROTO_SPP; X idp->idp_sna = nsp->nsp_laddr; X idp->idp_dna = nsp->nsp_faddr; X cb->s_sid = htons(spp_iss); X spp_iss += SPP_ISSINCR/2; X cb->s_alo = 1; X cb->s_cwnd = (sbspace(sb) * CUNIT) / cb->s_mtu; X cb->s_ssthresh = cb->s_cwnd; /* Try to expand fast to full complement X of large packets */ X cb->s_cwmx = (sb->sb_mbmax * CUNIT) / (2 * sizeof(struct spidp)); X cb->s_cwmx = MAX(cb->s_cwmx, cb->s_cwnd); X /* But allow for lots of little packets as well */ X} X X/* X * Close a SPIP control block: X * discard spp control block itself X * discard ns protocol control block X * wake up any sleepers X */ Xstruct sppcb * Xspp_close(cb) X register struct sppcb *cb; X{ X register struct spidp_q *s; X struct nspcb *nsp = cb->s_nspcb; X struct socket *so = nsp->nsp_socket; X register struct mbuf *m; X X s = cb->s_q.si_next; X while (s != &(cb->s_q)) { X s = s->si_next; X m = dtom(s->si_prev); X remque(s->si_prev); X m_freem(m); X } X (void) m_free(dtom(cb->s_idp)); X (void) m_free(dtom(cb)); X nsp->nsp_pcb = 0; X soisdisconnected(so); X ns_pcbdetach(nsp); X sppstat.spps_closed++; X return ((struct sppcb *)0); X} X/* X * Someday we may do level 3 handshaking X * to close a connection or send a xerox style error. X * For now, just close. X */ Xstruct sppcb * Xspp_usrclosed(cb) X register struct sppcb *cb; X{ X return (spp_close(cb)); X} Xstruct sppcb * Xspp_disconnect(cb) X register struct sppcb *cb; X{ X return (spp_close(cb)); X} X/* X * Drop connection, reporting X * the specified error. X */ Xstruct sppcb * Xspp_drop(cb, errno) X register struct sppcb *cb; X int errno; X{ X struct socket *so = cb->s_nspcb->nsp_socket; X X /* X * someday, in the xerox world X * we will generate error protocol packets X * announcing that the socket has gone away. X */ X if (TCPS_HAVERCVDSYN(cb->s_state)) { X sppstat.spps_drops++; X cb->s_state = TCPS_CLOSED; X /*(void) tcp_output(cb);*/ X } else X sppstat.spps_conndrops++; X so->so_error = errno; X return (spp_close(cb)); X} X Xspp_abort(nsp) X struct nspcb *nsp; X{ X X (void) spp_close((struct sppcb *)nsp->nsp_pcb); X} X Xint spp_backoff[SPP_MAXRXTSHIFT+1] = X { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 }; X/* X * Fast timeout routine for processing delayed acks X */ Xspp_fasttimo() X{ X register struct nspcb *nsp; X register struct sppcb *cb; X int s = splnet(); X X nsp = nspcb.nsp_next; X if (nsp) X for (; nsp != &nspcb; nsp = nsp->nsp_next) X if ((cb = (struct sppcb *)nsp->nsp_pcb) && X (cb->s_flags & SF_DELACK)) { X cb->s_flags &= ~SF_DELACK; X cb->s_flags |= SF_ACKNOW; X sppstat.spps_delack++; X (void) spp_output(cb, (struct mbuf *) 0); X } X splx(s); X} X X/* X * spp protocol timeout routine called every 500 ms. X * Updates the timers in all active pcb's and X * causes finite state machine actions if timers expire. X */ Xspp_slowtimo() X{ X register struct nspcb *ip, *ipnxt; X register struct sppcb *cb; X int s = splnet(); X register int i; X X /* X * Search through tcb's and update active timers. X */ X ip = nspcb.nsp_next; X if (ip == 0) { X splx(s); X return; X } X while (ip != &nspcb) { X cb = nstosppcb(ip); X ipnxt = ip->nsp_next; X if (cb == 0) X goto tpgone; X for (i = 0; i < SPPT_NTIMERS; i++) { X if (cb->s_timer[i] && --cb->s_timer[i] == 0) { X (void) spp_usrreq(cb->s_nspcb->nsp_socket, X PRU_SLOWTIMO, (struct mbuf *)0, X (struct mbuf *)i, (struct mbuf *)0); X if (ipnxt->nsp_prev != ip) X goto tpgone; X } X } X cb->s_idle++; X if (cb->s_rtt) X cb->s_rtt++; Xtpgone: X ip = ipnxt; X } X spp_iss += SPP_ISSINCR/PR_SLOWHZ; /* increment iss */ X splx(s); X} X/* X * SPP timer processing. X */ Xstruct sppcb * Xspp_timers(cb, timer) X register struct sppcb *cb; X int timer; X{ X long rexmt; X int win; X X cb->s_force = 1 + timer; X switch (timer) { X X /* X * 2 MSL timeout in shutdown went off. TCP deletes connection X * control block. X */ X case SPPT_2MSL: X printf("spp: SPPT_2MSL went off for no reason\n"); X cb->s_timer[timer] = 0; X break; X X /* X * Retransmission timer went off. Message has not X * been acked within retransmit interval. Back off X * to a longer retransmit interval and retransmit one packet. X */ X case SPPT_REXMT: X if (++cb->s_rxtshift > SPP_MAXRXTSHIFT) { X cb->s_rxtshift = SPP_MAXRXTSHIFT; X sppstat.spps_timeoutdrop++; X cb = spp_drop(cb, ETIMEDOUT); X break; X } X sppstat.spps_rexmttimeo++; X rexmt = ((cb->s_srtt >> 2) + cb->s_rttvar) >> 1; X rexmt *= spp_backoff[cb->s_rxtshift]; X SPPT_RANGESET(cb->s_rxtcur, rexmt, SPPTV_MIN, SPPTV_REXMTMAX); X cb->s_timer[SPPT_REXMT] = cb->s_rxtcur; X /* X * If we have backed off fairly far, our srtt X * estimate is probably bogus. Clobber it X * so we'll take the next rtt measurement as our srtt; X * move the current srtt into rttvar to keep the current X * retransmit times until then. X */ X if (cb->s_rxtshift > SPP_MAXRXTSHIFT / 4 ) { X cb->s_rttvar += (cb->s_srtt >> 2); X cb->s_srtt = 0; X } X cb->s_snxt = cb->s_rack; X /* X * If timing a packet, stop the timer. X */ X cb->s_rtt = 0; X /* X * See very long discussion in tcp_timer.c about congestion X * window and sstrhesh X */ X win = MIN(cb->s_swnd, (cb->s_cwnd/CUNIT)) / 2; X if (win < 2) X win = 2; X cb->s_cwnd = CUNIT; X cb->s_ssthresh = win * CUNIT; X (void) spp_output(cb, (struct mbuf *) 0); X break; X X /* X * Persistance timer into zero window. X * Force a probe to be sent. X */ X case SPPT_PERSIST: X sppstat.spps_persisttimeo++; X spp_setpersist(cb); X (void) spp_output(cb, (struct mbuf *) 0); X break; X X /* X * Keep-alive timer went off; send something X * or drop connection if idle for too long. X */ X case SPPT_KEEP: X sppstat.spps_keeptimeo++; X if (cb->s_state < TCPS_ESTABLISHED) X goto dropit; X if (cb->s_nspcb->nsp_socket->so_options & SO_KEEPALIVE) { X if (cb->s_idle >= SPPTV_MAXIDLE) X goto dropit; X sppstat.spps_keepprobe++; X (void) spp_output(cb, (struct mbuf *) 0); X } else X cb->s_idle = 0; X cb->s_timer[SPPT_KEEP] = SPPTV_KEEP; X break; X dropit: X sppstat.spps_keepdrops++; X cb = spp_drop(cb, ETIMEDOUT); X break; X } X return (cb); X} X#ifndef lint Xint SppcbSize = sizeof (struct sppcb); Xint NspcbSize = sizeof (struct nspcb); X#endif lint END-of-netns/spp_usrreq.c echo x - netns/spp_var.h sed 's/^X//' >netns/spp_var.h << 'END-of-netns/spp_var.h' X/* X * Copyright (c) 1984, 1985, 1986, 1987 Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that this notice is preserved and that due credit is given X * to the University of California at Berkeley. The name of the University X * may not be used to endorse or promote products derived from this X * software without specific prior written permission. This software X * is provided ``as is'' without express or implied warranty. X * X * @(#)spp_var.h 7.4 (Berkeley) 3/12/88 X */ X X/* X * Sp control block, one per connection X */ Xstruct sppcb { X struct spidp_q s_q; /* queue for out-of-order receipt */ X struct nspcb *s_nspcb; /* backpointer to internet pcb */ X u_char s_state; X u_char s_flags; X#define SF_ACKNOW 0x01 /* Ack peer immediately */ X#define SF_DELACK 0x02 /* Ack, but try to delay it */ X#define SF_HI 0x04 /* Show headers on input */ X#define SF_HO 0x08 /* Show headers on output */ X#define SF_PI 0x10 /* Packet (datagram) interface */ X#define SF_WIN 0x20 /* Window info changed */ X#define SF_RXT 0x40 /* Rxt info changed */ X#define SF_RVD 0x80 /* Calling from read usrreq routine */ X u_short s_mtu; /* Max packet size for this stream */ X/* use sequence fields in headers to store sequence numbers for this X connection */ X struct idp *s_idp; X struct sphdr s_shdr; /* prototype header to transmit */ X#define s_cc s_shdr.sp_cc /* connection control (for EM bit) */ X#define s_dt s_shdr.sp_dt /* datastream type */ X#define s_sid s_shdr.sp_sid /* source connection identifier */ X#define s_did s_shdr.sp_did /* destination connection identifier */ X#define s_seq s_shdr.sp_seq /* sequence number */ X#define s_ack s_shdr.sp_ack /* acknowledge number */ X#define s_alo s_shdr.sp_alo /* allocation number */ X#define s_dport s_idp->idp_dna.x_port /* where we are sending */ X struct sphdr s_rhdr; /* last received header (in effect!)*/ X u_short s_rack; /* their acknowledge number */ X u_short s_ralo; /* their allocation number */ X u_short s_smax; /* highest packet # we have sent */ X u_short s_snxt; /* which packet to send next */ X X/* congestion control */ X#define CUNIT 1024 /* scaling for ... */ X int s_cwnd; /* Congestion-controlled window */ X /* in packets * CUNIT */ X short s_swnd; /* == tcp snd_wnd, in packets */ X short s_smxw; /* == tcp max_sndwnd */ X /* difference of two spp_seq's can be X no bigger than a short */ X u_short s_swl1; /* == tcp snd_wl1 */ X u_short s_swl2; /* == tcp snd_wl2 */ X int s_cwmx; /* max allowable cwnd */ X int s_ssthresh; /* s_cwnd size threshhold for X * slow start exponential-to- X * linear switch */ X/* transmit timing stuff X * srtt and rttvar are stored as fixed point, for convenience in smoothing. X * srtt has 3 bits to the right of the binary point, rttvar has 2. X */ X short s_idle; /* time idle */ X short s_timer[SPPT_NTIMERS]; /* timers */ X short s_rxtshift; /* log(2) of rexmt exp. backoff */ X short s_rxtcur; /* current retransmit value */ X u_short s_rtseq; /* packet being timed */ X short s_rtt; /* timer for round trips */ X short s_srtt; /* averaged timer */ X short s_rttvar; /* variance in round trip time */ X char s_force; /* which timer expired */ X char s_dupacks; /* counter to intuit xmt loss */ X X/* out of band data */ X char s_oobflags; X#define SF_SOOB 0x08 /* sending out of band data */ X#define SF_IOOB 0x10 /* receiving out of band data */ X char s_iobc; /* input characters */ X/* debug stuff */ X u_short s_want; /* Last candidate for sending */ X char s_outx; /* exit taken from spp_output */ X char s_inx; /* exit taken from spp_input */ X}; X X#define nstosppcb(np) ((struct sppcb *)(np)->nsp_pcb) X#define sotosppcb(so) (nstosppcb(sotonspcb(so))) X Xstruct sppstat { X long spps_connattempt; /* connections initiated */ X long spps_accepts; /* connections accepted */ X long spps_connects; /* connections established */ X long spps_drops; /* connections dropped */ X long spps_conndrops; /* embryonic connections dropped */ X long spps_closed; /* conn. closed (includes drops) */ X long spps_segstimed; /* segs where we tried to get rtt */ X long spps_rttupdated; /* times we succeeded */ X long spps_delack; /* delayed acks sent */ X long spps_timeoutdrop; /* conn. dropped in rxmt timeout */ X long spps_rexmttimeo; /* retransmit timeouts */ X long spps_persisttimeo; /* persist timeouts */ X long spps_keeptimeo; /* keepalive timeouts */ X long spps_keepprobe; /* keepalive probes sent */ X long spps_keepdrops; /* connections dropped in keepalive */ X X long spps_sndtotal; /* total packets sent */ X long spps_sndpack; /* data packets sent */ X long spps_sndbyte; /* data bytes sent */ X long spps_sndrexmitpack; /* data packets retransmitted */ X long spps_sndrexmitbyte; /* data bytes retransmitted */ X long spps_sndacks; /* ack-only packets sent */ X long spps_sndprobe; /* window probes sent */ X long spps_sndurg; /* packets sent with URG only */ X long spps_sndwinup; /* window update-only packets sent */ X long spps_sndctrl; /* control (SYN|FIN|RST) packets sent */ X long spps_sndvoid; /* couldn't find requested packet*/ X X long spps_rcvtotal; /* total packets received */ X long spps_rcvpack; /* packets received in sequence */ X long spps_rcvbyte; /* bytes received in sequence */ X long spps_rcvbadsum; /* packets received with ccksum errs */ X long spps_rcvbadoff; /* packets received with bad offset */ X long spps_rcvshort; /* packets received too short */ X long spps_rcvduppack; /* duplicate-only packets received */ X long spps_rcvdupbyte; /* duplicate-only bytes received */ X long spps_rcvpartduppack; /* packets with some duplicate data */ X long spps_rcvpartdupbyte; /* dup. bytes in part-dup. packets */ X long spps_rcvoopack; /* out-of-order packets received */ X long spps_rcvoobyte; /* out-of-order bytes received */ X long spps_rcvpackafterwin; /* packets with data after window */ X long spps_rcvbyteafterwin; /* bytes rcvd after window */ X long spps_rcvafterclose; /* packets rcvd after "close" */ X long spps_rcvwinprobe; /* rcvd window probe packets */ X long spps_rcvdupack; /* rcvd duplicate acks */ X long spps_rcvacktoomuch; /* rcvd acks for unsent data */ X long spps_rcvackpack; /* rcvd ack packets */ X long spps_rcvackbyte; /* bytes acked by rcvd acks */ X long spps_rcvwinupd; /* rcvd window update packets */ X}; Xstruct spp_istat { X short hdrops; X short badsum; X short badlen; X short slotim; X short fastim; X short nonucn; X short noconn; X short notme; X short wrncon; X short bdreas; X short gonawy; X short notyet; X short lstdup; X struct sppstat newstats; X}; X X#ifdef KERNEL Xstruct spp_istat spp_istat; X X/* Following was struct sppstat sppstat; */ X#ifndef sppstat X#define sppstat spp_istat.newstats X#endif X Xu_short spp_iss; Xextern struct sppcb *spp_close(), *spp_disconnect(), X *spp_usrclosed(), *spp_timers(), *spp_drop(); X#endif X X#define SPP_ISSINCR 128 X/* X * SPP sequence numbers are 16 bit integers operated X * on with modular arithmetic. These macros can be X * used to compare such integers. X */ X#ifdef sun Xshort xnsCbug; X#define SSEQ_LT(a,b) ((xnsCbug = (short)((a)-(b))) < 0) X#define SSEQ_LEQ(a,b) ((xnsCbug = (short)((a)-(b))) <= 0) X#define SSEQ_GT(a,b) ((xnsCbug = (short)((a)-(b))) > 0) X#define SSEQ_GEQ(a,b) ((xnsCbug = (short)((a)-(b))) >= 0) X#else X#define SSEQ_LT(a,b) (((short)((a)-(b))) < 0) X#define SSEQ_LEQ(a,b) (((short)((a)-(b))) <= 0) X#define SSEQ_GT(a,b) (((short)((a)-(b))) > 0) X#define SSEQ_GEQ(a,b) (((short)((a)-(b))) >= 0) X#endif END-of-netns/spp_var.h exit