date and time created 88/12/14 15:30:03 by sklower
authorKeith Sklower <sklower@ucbvax.Berkeley.EDU>
Thu, 15 Dec 1988 07:30:03 +0000 (23:30 -0800)
committerKeith Sklower <sklower@ucbvax.Berkeley.EDU>
Thu, 15 Dec 1988 07:30:03 +0000 (23:30 -0800)
SCCS-vsn: sys/netiso/tp_iso.c 7.1

usr/src/sys/netiso/tp_iso.c [new file with mode: 0644]

diff --git a/usr/src/sys/netiso/tp_iso.c b/usr/src/sys/netiso/tp_iso.c
new file mode 100644 (file)
index 0000000..026dc4d
--- /dev/null
@@ -0,0 +1,634 @@
+/***********************************************************
+               Copyright IBM Corporation 1987
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* 
+ * ARGO TP
+ * $Header: tp_iso.c,v 5.3 88/11/18 17:27:57 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_iso.c,v $
+ *
+ * Here is where you find the iso-dependent code.  We've tried
+ * keep all net-level and (primarily) address-family-dependent stuff
+ * out of the tp source, and everthing here is reached indirectly
+ * through a switch table (struct nl_protosw *) tpcb->tp_nlproto 
+ * (see tp_pcb.c). 
+ * The routines here are:
+ *             iso_getsufx: gets transport suffix out of an isopcb structure.
+ *             iso_putsufx: put transport suffix into an isopcb structure.
+ *             iso_putnetaddr: put a whole net addr into an isopcb.
+ *             iso_getnetaddr: get a whole net addr from an isopcb.
+ *             iso_recycle_suffix: clear suffix for reuse in isopcb
+ *             tpclnp_ctlinput: handle ER CNLPdu : icmp-like stuff
+ *             tpclnp_mtu: figure out what size tpdu to use
+ *             tpclnp_input: take a pkt from clnp, strip off its clnp header, 
+ *                             give to tp
+ *             tpclnp_output_dg: package a pkt for clnp given 2 addresses & some data
+ *             tpclnp_output: package a pkt for clnp given an isopcb & some data
+ */
+
+#ifndef lint
+static char *rcsid = "$Header: tp_iso.c,v 5.3 88/11/18 17:27:57 nhall Exp $";
+#endif lint
+
+#ifdef ISO
+
+#include "../h/types.h"
+#include "../h/socket.h"
+#include "../h/socketvar.h"
+#include "../h/domain.h"
+#include "../h/mbuf.h"
+#include "../h/errno.h"
+#include "../h/time.h"
+#include "../net/if.h"
+#include "../net/route.h"
+#include "../h/protosw.h"
+
+#include "../netiso/tp_param.h"
+#include "../netiso/argo_debug.h"
+#include "../netiso/tp_stat.h"
+#include "../netiso/tp_pcb.h"
+#include "../netiso/tp_trace.h"
+#include "../netiso/tp_stat.h"
+#include "../netiso/tp_tpdu.h"
+#include "../netiso/tp_clnp.h"
+
+/*
+ * CALLED FROM:
+ *     pr_usrreq() on PRU_BIND, PRU_CONNECT, PRU_ACCEPT, and PRU_PEERADDR
+ * FUNCTION, ARGUMENTS, and RETURN VALUE:
+ *     Return a transport suffix from an isopcb structure (inp).
+ *  (CAST TO AN INT)
+ *     The argument (which) takes the value TP_LOCAL or TP_FOREIGN.
+ */
+
+short
+iso_getsufx(isop,  which)
+       struct isopcb *isop;
+       int which;
+{
+       switch (which) {
+       case TP_LOCAL:
+               return  htons(isop->isop_laddr.siso_tsuffix);
+
+       case TP_FOREIGN:
+               return  htons(isop->isop_faddr.siso_tsuffix);
+       }
+}
+
+/* CALLED FROM:
+ *     tp_newsocket(); i.e., when a connection is being established by an
+ *     incoming CR_TPDU.
+ *
+ * FUNCTION, ARGUMENTS:
+ *     Put a transport suffix (found in name) into an isopcb structure (isop).
+ *     The argument (which) takes the value TP_LOCAL or TP_FOREIGN.
+ */
+void
+iso_putsufx(isop, name, which)
+       struct isopcb *isop;
+       struct sockaddr_iso *name;
+       int which;
+{
+       switch (which) {
+       case TP_LOCAL:
+               isop->isop_lport = ntohs(name->siso_tsuffix);
+               break;
+       case TP_FOREIGN:
+               isop->isop_fport = ntohs(name->siso_tsuffix);
+               break;
+       }
+}
+
+/*
+ * CALLED FROM:
+ *     tp.trans whenever we go into REFWAIT state.
+ * FUNCTION and ARGUMENT:
+ *      Called when a ref is frozen, to allow the suffix to be reused. 
+ *     (isop) is the net level pcb.  This really shouldn't have to be
+ *     done in a NET level pcb but... for the internet world that just
+ *     the way it is done in BSD...
+ *     The alternative is to have the port unusable until the reference
+ *     timer goes off.
+ */
+void
+iso_recycle_tsuffix(isop)
+       struct isopcb   *isop;
+{
+       isop->isop_laddr.siso_tsuffix = isop->isop_faddr.siso_tsuffix = 0;
+}
+
+/*
+ * CALLED FROM:
+ *     tp_newsocket(); i.e., when a connection is being established by an
+ *     incoming CR_TPDU.
+ *
+ * FUNCTION and ARGUMENTS:
+ *     Copy a whole net addr from a struct sockaddr (name).
+ *     into an isopcb (isop).
+ *     The argument (which) takes values TP_LOCAL or TP_FOREIGN
+ */ 
+void
+iso_putnetaddr(isop, name, which)
+       register struct isopcb  *isop;
+       struct sockaddr_iso     *name;
+       int which;
+{
+       switch (which) {
+       case TP_LOCAL:
+               isop->isop_laddr.siso_family = AF_ISO;
+               bcopy((caddr_t)&name->siso_addr,
+                       (caddr_t)&isop->isop_laddr.siso_addr, sizeof(struct iso_addr));
+               IFDEBUG(D_TPISO)
+                       printf("PUT TP_LOCAL addr\n");
+                       dump_isoaddr(&isop->isop_laddr);
+               ENDDEBUG
+               break;
+       case TP_FOREIGN:
+               isop->isop_faddr.siso_family = AF_ISO;
+               if( name != (struct sockaddr_iso *)0 ) {
+                       bcopy((caddr_t)&name->siso_addr, 
+                               (caddr_t)&isop->isop_faddr.siso_addr, sizeof(struct iso_addr));
+               }
+               IFDEBUG(D_TPISO)
+                       printf("PUT TP_FOREIGN addr\n");
+                       dump_isoaddr(&isop->isop_faddr);
+               ENDDEBUG
+       }
+}
+
+/*
+ * CALLED FROM:
+ *  pr_usrreq() PRU_SOCKADDR, PRU_ACCEPT, PRU_PEERADDR
+ * FUNCTION and ARGUMENTS:
+ *     Copy a whole net addr from an isopcb (isop) into
+ *     a struct sockaddr (name).
+ *     The argument (which) takes values TP_LOCAL or TP_FOREIGN.
+ */ 
+
+void
+iso_getnetaddr( isop, name, which)
+       struct isopcb *isop;
+       struct sockaddr_iso *name;
+       int which;
+{
+       switch (which) {
+       case TP_LOCAL:
+               bcopy( (caddr_t)&isop->isop_laddr.siso_addr, 
+                       (caddr_t)&name->siso_addr, sizeof(struct iso_addr));
+               break;
+
+       case TP_FOREIGN:
+               bcopy( (caddr_t)&isop->isop_faddr.siso_addr, 
+                       (caddr_t)&name->siso_addr, sizeof(struct iso_addr));
+               break;
+       }
+}
+
+/*
+ * CALLED FROM:
+ *  tp_input() on incoming CR, CC, and pr_usrreq() for PRU_CONNECT
+ * FUNCTION, ARGUMENTS, SIDE EFFECTS and RETURN VALUE:
+ * Determine the proper maximum transmission unit, i.e., MTU, to use, given
+ * a) the header size for the network protocol and the max transmission
+ *       unit on the subnet interface, determined from the information in (isop),
+ * b) the max size negotiated so far (negot)
+ * c) the window size used by the tp connection (found in so),
+ *
+ * The result is put in the integer *size in its integer form and in
+ * *negot in its logarithmic form.  
+ * 
+ * The rules are:
+ * a) can only negotiate down from the value found in *negot.
+ * b) the MTU must be < the windowsize,
+ * c) If src and dest are on the same net,
+ *       we will negotiate the closest size larger than  MTU but really USE 
+ *    the actual device mtu - ll hdr sizes.
+ *   otherwise we negotiate the closest size smaller than MTU - ll hdr sizes.
+ */
+
+void
+tpclnp_mtu(so, isop, size, negot )
+       struct socket *so;
+       struct isopcb *isop;
+       int *size;
+       u_char *negot;
+{
+       struct ifnet *ifp;
+       register int i;
+       int windowsize = so->so_rcv.sb_hiwat;
+       int clnp_size;
+       int sizeismtu = 0;
+
+       struct ifnet    *iso_routeifp();
+
+       IFDEBUG(D_CONN)
+               printf("tpclnp_mtu(0x%x,0x%x,0x%x,0x%x)\n", so, isop, size, negot);
+       ENDDEBUG
+       IFTRACE(D_CONN)
+               tptrace(TPPTmisc, "ENTER GET MTU: size negot \n",*size, *negot, 0, 0);
+       ENDTRACE
+
+       *size = 1 << *negot;
+
+       if( *size > windowsize ) {
+               *size = windowsize;
+       }
+
+       if ((ifp = iso_routeifp(&isop->isop_faddr)) == (struct ifnet *)0)
+               return;
+
+       /* TODO - make this indirect off the socket structure to the
+        * network layer to get headersize
+        */
+       clnp_size = clnp_hdrsize(isop->isop_laddr.siso_addr.isoa_len);
+
+       if(*size > ifp->if_mtu - clnp_size) {
+               *size = ifp->if_mtu - clnp_size;
+               sizeismtu = 1;
+       }
+       IFTRACE(D_CONN)
+               tptrace(TPPTmisc, "GET MTU MID: tpcb size negot i \n",
+               *size, *negot, i, 0);
+       ENDTRACE
+
+       /* have to transform size to the log2 of size */
+       for(i=TP_MIN_TPDUSIZE; (i<TP_MAX_TPDUSIZE && ((1<<i) <= *size)) ; i++)
+               ;
+       i--;
+
+       /* are we on the same LAN? if so, negotiate one tpdu size larger,
+        * and actually send the real mtu size
+        */
+       /* PHASE2: replace with iso_on_localnet(&isop->isop_faddr);
+        * or something along those lines
+        */
+       if ( iso_netmatch(&isop->isop_laddr, &isop->isop_faddr) && sizeismtu ) {
+               i++;
+       } else {
+               *size = 1<<i;
+       }
+       *negot = i;
+
+       IFDEBUG(D_CONN)
+               printf("GET MTU RETURNS: ifp %s size 0x%x negot 0x%x\n",
+               ifp->if_name,   *size, *negot);
+       ENDDEBUG
+       IFTRACE(D_CONN)
+               tptrace(TPPTmisc, "EXIT GET MTU: tpcb size negot \n",
+               *size, *negot, 0, 0);
+       ENDTRACE
+}
+
+
+/*
+ * CALLED FROM:
+ *  tp_emit()
+ * FUNCTION and ARGUMENTS:
+ *  Take a packet(m0) from tp and package it so that clnp will accept it.
+ *  This means prepending space for the clnp header and filling in a few
+ *  of the fields.
+ *  inp is the isopcb structure; datalen is the length of the data in the
+ *  mbuf string m0.
+ * RETURN VALUE:
+ *  whatever (E*) is returned form the net layer output routine.
+ */
+
+int
+tpclnp_output(isop, m0, datalen, nochksum)
+       struct isopcb           *isop;
+       struct mbuf             *m0;
+       int                             datalen;
+       int                                     nochksum;
+{
+       IncStat(ts_tpdu_sent);
+
+       IFDEBUG(D_TPISO)
+               struct tpdu *hdr = mtod(m0, struct tpdu *);
+
+               printf(
+"abt to call clnp_output: datalen 0x%x, hdr.li 0x%x, hdr.dutype 0x%x nocsum x%x dst addr:\n",
+                       datalen,
+                       (int)hdr->tpdu_li, (int)hdr->tpdu_type, nochksum);
+               dump_isoaddr(&isop->isop_faddr);
+               printf("\nsrc addr:\n");
+               dump_isoaddr(&isop->isop_laddr);
+               dump_mbuf(m0, "at tpclnp_output");
+       ENDDEBUG
+
+       return 
+               clnp_output(m0, isop, datalen, nochksum?CLNP_NO_CKSUM:0 /* flags */);
+}
+
+/*
+ * CALLED FROM:
+ *  tp_error_emit()
+ * FUNCTION and ARGUMENTS:
+ *  This is a copy of tpclnp_output that takes the addresses
+ *  instead of a pcb.  It's used by the tp_error_emit, when we
+ *  don't have an iso_pcb with which to call the normal output rtn.
+ * RETURN VALUE:
+ *  ENOBUFS or
+ *  whatever (E*) is returned form the net layer output routine.
+ */
+
+int
+tpclnp_output_dg(laddr, faddr, m0, datalen, ro, nochksum)
+       struct iso_addr         *laddr, *faddr;
+       struct mbuf             *m0;
+       int                             datalen;
+       struct route            *ro;
+       int                                     nochksum;
+{
+       struct isopcb           tmppcb;
+       struct iso_addr         *isoa;
+       int                                     err;
+       int                                     flags;
+
+       IFDEBUG(D_TPISO)
+               printf("tpclnp_output_dg  datalen 0x%x m0 0x%x\n", datalen, m0);
+       ENDDEBUG
+
+       /*
+        *      Fill in minimal portion of isopcb so that clnp can send the
+        *      packet.
+        */
+       bzero((caddr_t)&tmppcb, sizeof(tmppcb));
+       isoa = &(tmppcb.isop_laddr.siso_addr);
+       bcopy((caddr_t)laddr, (caddr_t)isoa, sizeof (struct iso_addr));
+       isoa = &(tmppcb.isop_faddr.siso_addr);
+       bcopy((caddr_t)faddr, (caddr_t)isoa, sizeof (struct iso_addr));
+
+       IFDEBUG(D_TPISO)
+               printf("tpclnp_output_dg  faddr: \n");
+               dump_isoaddr(&tmppcb.isop_faddr);
+               printf("\ntpclnp_output_dg  laddr: \n");
+               dump_isoaddr(&tmppcb.isop_laddr);
+               printf("\n");
+       ENDDEBUG
+
+       /*
+        *      Do not use packet cache since this is a one shot error packet
+        */
+       flags = (CLNP_NOCACHE|(nochksum?CLNP_NO_CKSUM:0));
+
+       IncStat(ts_tpdu_sent);
+
+       err = clnp_output(m0, &tmppcb, datalen, flags);
+       
+       /*
+        *      Free route allocated by clnp (if the route was indeed allocated)
+        */
+       if (tmppcb.isop_route.ro_rt)
+               RTFREE(tmppcb.isop_route.ro_rt);
+       
+       return(err);
+}
+
+/*
+ * CALLED FROM:
+ *     clnp's input routine, indirectly through the protosw.
+ * FUNCTION and ARGUMENTS:
+ * Take a packet (m) from clnp, strip off the clnp header and give it to tp
+ * No return value.  
+ */
+ProtoHook
+tpclnp_input(m, faddr, laddr, clnp_len)
+       struct mbuf *m;
+       struct iso_addr *faddr, *laddr;
+       int clnp_len;
+{
+       struct sockaddr_iso src, dst;
+       int s = splnet();
+
+       IncStat(ts_pkt_rcvd);
+
+       IFDEBUG(D_TPINPUT)
+               printf("tpclnp_input: m 0x%x clnp_len 0x%x\n", m, clnp_len);
+               dump_mbuf(m, "at tpclnp_input");
+       ENDDEBUG
+       /*
+        * CLNP gives us an mbuf chain WITH the clnp header pulled up,
+        * and the length of the clnp header.
+        * First, strip off the Clnp header. leave the mbuf there for the
+        * pullup that follows.
+        */
+
+       m->m_len -= clnp_len;
+       m->m_off += clnp_len;
+
+       m = (struct mbuf *)tp_inputprep(m);
+
+       IFDEBUG(D_TPINPUT)
+               dump_mbuf(m, "after tpclnp_input both pullups");
+       ENDDEBUG
+
+       src.siso_family = dst.siso_family = AF_ISO;
+       bcopy(faddr, &src.siso_addr, sizeof(struct iso_addr));
+       bcopy(laddr, &dst.siso_addr, sizeof(struct iso_addr));
+
+       IFDEBUG(D_TPISO)
+               printf("calling tp_input: &src 0x%x  &dst 0x%x, src addr:\n", 
+                       &src, &dst);
+               printf(" dst addr:\n");
+               dump_isoaddr(&src);
+               dump_isoaddr(&dst);
+       ENDDEBUG
+
+       (void) tp_input(m, &src, &dst, 0, tpclnp_output_dg);
+
+       IFDEBUG(D_QUENCH)
+               { 
+                       if(time.tv_usec & 0x4 && time.tv_usec & 0x40) {
+                               printf("tpclnp_input: FAKING %s\n", 
+                                       tp_stat.ts_pkt_rcvd & 0x1?"QUENCH":"QUENCH2");
+                               if(tp_stat.ts_pkt_rcvd & 0x1) {
+                                       tpclnp_ctlinput(PRC_QUENCH, &src);
+                               } else {
+                                       tpclnp_ctlinput(PRC_QUENCH2, &src);
+                               }
+                       }
+               }
+       ENDDEBUG
+
+       splx(s);
+       return 0;
+
+discard:
+       IFDEBUG(D_TPINPUT)
+               printf("tpclnp_input DISCARD\n");
+       ENDDEBUG
+       IFTRACE(D_TPINPUT)
+               tptrace(TPPTmisc, "tpclnp_input DISCARD m",  m,0,0,0);
+       ENDTRACE
+       m_freem(m);
+       IncStat(ts_recv_drop);
+       splx(s);
+
+       return 0;
+}
+
+ProtoHook
+iso_rtchange()
+{
+       return 0;
+}
+
+/*
+ * CALLED FROM:
+ *  tpclnp_ctlinput()
+ * FUNCTION and ARGUMENTS:
+ *  find the tpcb pointer and pass it to tp_quench
+ */
+void
+tpiso_decbit(isop)
+       struct isopcb *isop;
+{
+       tp_quench( isop->isop_socket->so_tpcb, PRC_QUENCH2 );
+}
+/*
+ * CALLED FROM:
+ *  tpclnp_ctlinput()
+ * FUNCTION and ARGUMENTS:
+ *  find the tpcb pointer and pass it to tp_quench
+ */
+void
+tpiso_quench(isop)
+       struct isopcb *isop;
+{
+       tp_quench( isop->isop_socket->so_tpcb, PRC_QUENCH );
+}
+
+/*
+ * CALLED FROM:
+ *  The network layer through the protosw table.
+ * FUNCTION and ARGUMENTS:
+ *     When clnp an ICMP-like msg this gets called.
+ *     It either returns an error status to the user or
+ *     it causes all connections on this address to be aborted
+ *     by calling the appropriate xx_notify() routine.
+ *     (cmd) is the type of ICMP error.   
+ *     (siso) is the address of the guy who sent the ER CLNPDU
+ */
+ProtoHook
+tpclnp_ctlinput(cmd, siso)
+       int cmd;
+       struct sockaddr_iso *siso;
+{
+       extern u_char inetctlerrmap[];
+       extern ProtoHook tpiso_abort();
+       extern ProtoHook iso_rtchange();
+       extern ProtoHook tpiso_reset();
+
+       IFDEBUG(D_TPINPUT)
+               printf("tpclnp_ctlinput: cmd 0x%x addr: ", cmd);
+               dump_isoaddr(siso);
+               printf("\n");
+       ENDDEBUG
+
+       if (cmd < 0 || cmd > PRC_NCMDS)
+               return 0;
+       switch (cmd) {
+
+               case    PRC_QUENCH2:
+                       iso_pcbnotify(&tp_isopcb, &siso->siso_addr, 0, tpiso_decbit);
+                       break;
+
+               case    PRC_QUENCH:
+                       iso_pcbnotify(&tp_isopcb, &siso->siso_addr, 0, tpiso_quench);
+                       break;
+
+               case    PRC_TIMXCEED_REASS:
+               case    PRC_ROUTEDEAD:
+                       iso_pcbnotify(&tp_isopcb, &siso->siso_addr, 0, tpiso_reset);
+                       break;
+
+               case    PRC_HOSTUNREACH:
+               case    PRC_UNREACH_NET:
+               case    PRC_IFDOWN:
+               case    PRC_HOSTDEAD:
+                       iso_pcbnotify(&tp_isopcb, &siso->siso_addr,
+                                       (int)inetctlerrmap[cmd], iso_rtchange);
+                       break;
+
+               default:
+               /*
+               case    PRC_MSGSIZE:
+               case    PRC_UNREACH_HOST:
+               case    PRC_UNREACH_PROTOCOL:
+               case    PRC_UNREACH_PORT:
+               case    PRC_UNREACH_NEEDFRAG:
+               case    PRC_UNREACH_SRCFAIL:
+               case    PRC_REDIRECT_NET:
+               case    PRC_REDIRECT_HOST:
+               case    PRC_REDIRECT_TOSNET:
+               case    PRC_REDIRECT_TOSHOST:
+               case    PRC_TIMXCEED_INTRANS:
+               case    PRC_PARAMPROB:
+               */
+               iso_pcbnotify(&tp_isopcb, &siso->siso_addr, 
+                       (int)inetctlerrmap[cmd], tpiso_abort);
+               break;
+       }
+       return 0;
+}
+
+/*
+ * These next 2 routines are
+ * CALLED FROM:
+ *     xxx_notify() from tp_ctlinput() when
+ *  net level gets some ICMP-equiv. type event.
+ * FUNCTION and ARGUMENTS:
+ *  Cause the connection to be aborted with some sort of error
+ *  reason indicating that the network layer caused the abort.
+ *  Fakes an ER TPDU so we can go through the driver.
+ *  abort always aborts the TP connection.
+ *  reset may or may not, depending on the TP class that's in use.
+ */
+ProtoHook
+tpiso_abort(isop)
+       struct isopcb *isop;
+{
+       struct tp_event e;
+
+       IFDEBUG(D_CONN)
+               printf("tpiso_abort 0x%x\n", isop);
+       ENDDEBUG
+       e.ev_number = ER_TPDU;
+       e.ATTR(ER_TPDU).e_reason = ECONNABORTED;
+       return  tp_driver((struct tp_pcb *)isop->isop_socket->so_tpcb, &e);
+}
+
+ProtoHook
+tpiso_reset(isop)
+       struct isopcb *isop;
+{
+       struct tp_event e;
+
+       e.ev_number = T_NETRESET;
+       return tp_driver((struct tp_pcb *)isop->isop_socket->so_tpcb, &e);
+
+}
+
+#endif ISO