V8/usr/sys/chaosld/chrcv.c

/*
 *             C H  R C V
 *
 * Chaosnet line discipline - handle a received packet.
 * Chrcv() is called by the service routine for the RD queue.
 *
 *
 * (c) Copyright 1984  Nirvonics, Inc.
 *
 * Written by Kurt Gollhardt
 * Last update Fri Nov 16 11:16:20 1984
 *
 * This software is the property of Nirvonics, Inc.
 * All rights reserved.
 *
 */

#include "ch.h"
#if NCH > 0
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/stream.h"
#include "../h/conf.h"
#include "../chaosld/types.h"
#include "../chaosld/constants.h"
#include "../chaosld/globals.h"

#define send_los(pkt,str)     sendlos(pkt,str,sizeof(str)-1)

#define STS_AGE	    hz/4      /* Minimum pkt age for re-xmission on STS */


chrcv(q)
     struct queue   *q;
{
     register struct packet        *pkt;
     register struct connection    *conn;
     register struct sts_data      *sts;
     register unsigned   index;

     debug(DPKT,printf("Chrcv: "));
     if ((pkt = get_packet(q, 0)) == NOPKT)
          return;
     pkt->next = NOPKT;

     ch_busy++;
     if (pkt->pk_op == MNTOP)
          free_packet(pkt);
     else if (pkt->pk_op == RFCOP)
          rcvrfc(pkt);
     else if (pkt->pk_op == BRDOP)
          rcvbrd(pkt);
     /*
      * Check for various flavors of bad indices
      */
     else if ((index = pkt->pk_didx.ci_tidx) >= NCH ||
               (conn = Chconn[index]) == NOCONN ||
	       pkt->pk_didx.ci_idx != conn->cn_lidx.ci_idx) {
          debug(DPKT|DABNOR,printf("Packet with bad index: %x, op:0%o\n",
               pkt->pk_didx, pkt->pk_op));
          send_los(pkt, "Connection doesn't exist");
     /*
      * Handle responses to our RFC
      */
     } else if (conn->cn_state == CSRFCSENT)
          switch(pkt->pk_op) {
          case OPNOP:
               debug(DCONN,printf("Conn #%x: OPEN received\n", conn->cn_lidx));
               /*
                * Mark the connection as open, saving his index
                */
               conn->cn_state = CSOPEN;
               conn->cn_fidx = pkt->pk_sidx;
               conn->cn_time = Chclock;
               /*
                * Indicate we have received AND "read" the OPEN
                * Read his window size, indicate that he has
                * received and acked the RFC, and acknowledge the OPN
                */
               conn->cn_rread = conn->cn_rlast = pkt->pk_pkn;
	       flatten(pkt);
	       sts = (struct sts_data *)pkt->data->rptr;
               conn->cn_twsize = sts->sts_rwsize;
               receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
               /*
                * Send him back an STS to get things going.
                */
               setsts(conn, pkt);
               reflect(pkt);
	       chd_newstate(conn);
               break;
          case CLSOP:
          case ANSOP:
          case FWDOP:
               debug(DCONN,printf("Conn #%x: CLOSE/ANS received for RFC\n", conn->cn_lidx));
               close_conn(conn, CSCLOSED, pkt);
               break;
          default:
               debug(DPKT|DABNOR,
                    printf("bad packet type for conn #%x in CSRFCSENT: %d\n",
                         conn->cn_lidx, pkt->pk_op));
               send_los(pkt, "Bad packet type reponse to RFC");
          }
     /*
      * Process a packet for an open connection
      */
     else if (conn->cn_state == CSOPEN) {
          conn->cn_time = Chclock;
          if (ISDATA(pkt))
               rcvdata(conn, pkt);
          else switch (pkt->pk_op) {
              case OPNOP:
               /*
                * Ignore duplicate opens.
                */
               debug(DPKT|DABNOR,printf("Duplicate open received\n"));
               free_packet(pkt);
               break;
          case SNSOP:
               debug(DPKT,prpkt(pkt, "SNS"));
               receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
               setsts(conn, pkt);
               reflect(pkt);
               break;
          case EOFOP:
               rcvdata(conn, pkt);
               break;
          case LOSOP:
          case CLSOP:
               debug(DCONN, printf("Close rcvd on %x\n", conn->cn_lidx));
               close_conn(conn, pkt->pk_op == CLSOP ? CSCLOSED : CSLOST,
                    pkt);
               break;
               /*
                * Uncontrolled data - queue it at the head of the rlist
                */
          case UNCOP:
               receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
	       ch_input(conn, pkt);  /* Send packet to pseudo-device */
               break;
          case STSOP:
               flatten(pkt);
	       sts = (struct sts_data *)pkt->data->rptr;
               if (sts->sts_rwsize > conn->cn_twsize)
                    chd_newout(conn);
               conn->cn_twsize = sts->sts_rwsize;
               receipt(conn, pkt->pk_ackn, sts->sts_receipt);
               free_packet(pkt);
	       chretran(conn, STS_AGE);  /* Re-xmit all but very recent pkts */
               break;
          default:
               debug(DPKT|DABNOR, printf("bad opcode:%d\n", pkt->pk_op));
               send_los(pkt, "Bad opcode");
               /* should we do close_conn here? */
          }
     /*
      * Connection is neither waiting for an OPEN nor OPEN.
      */
     } else {
          debug(DPKT|DABNOR,printf("Packet for conn #%x (not open) state=%d, op:%d\n", conn->cn_lidx, conn->cn_state, pkt->pk_op));
          send_los(pkt, "Connection is closed");
     }

     --ch_busy;
}

/*
 * Process a received data packet - or an EOF packet which is mostly treated
 * the same.
 */
rcvdata(conn, pkt)
     register struct connection    *conn;
     register struct packet        *pkt;
{
     register struct packet   *npkt;

     debug(DPKT,(prpkt(pkt,"DATA"),printf("\n")) );
     if (cmp_gt(pkt->pk_pkn, conn->cn_rread + conn->cn_rwsize)) {
          debug(DPKT|DABNOR,printf("Discarding data out of window\n"));
          free_packet(pkt);
          return;
     }
     receipt(conn, pkt->pk_ackn, pkt->pk_ackn);
     if (cmp_le(pkt->pk_pkn, conn->cn_rlast)) {
          debug(DPKT,printf("Duplicate data packet\n"));
          setsts(conn, pkt);
          reflect(pkt);
          return;
     }

     /*
      * Link the out of order list onto the new packet in case
      * it fills the gap between in-order and out-of-order lists
      * and to make it easy to grab all now-in-order packets from the
      * out-of-order list.
      */
     pkt->next = conn->cn_routorder;
     /*
      * Now transfer all in-order packets to the pseudo-device
      */
     for (npkt = pkt; npkt != NOPKT &&
                    npkt->pk_pkn == (unsigned short)(conn->cn_rlast + 1);
                    npkt = npkt->next) {
          conn->cn_rlast++;
          ch_input(conn, npkt);
     }
     /*
      * If we received any in-order pkts, check if spontaneous STS is needed
      */
     if (pkt != npkt) {
          debug(DPKT,printf("new ordered data packet\n"));
          conn->cn_routorder = npkt;
     } else {
     /*
      * Here we have received an out of order packet which must be
      * inserted into the out-of-order queue, in packet number order. 
      */
          for (npkt = pkt; (npkt = npkt->next) != NOPKT &&
                     cmp_gt(pkt->pk_pkn, npkt->pk_pkn); )
               pkt->next = npkt;     /* save the last pkt here */

          if (npkt != NOPKT && pkt->pk_pkn == npkt->pk_pkn) {
               debug(DPKT|DABNOR,
                    printf("Duplicate out of order packet\n"));
               pkt->next = NOPKT;
               setsts(conn, pkt);
               reflect(pkt);
          } else {
               if (npkt == conn->cn_routorder)
                    conn->cn_routorder = pkt;
               else
                    pkt->next->next = pkt;
               pkt->next = npkt;
               debug(DPKT|DABNOR,
                     printf("New out of order packet\n"));
          }
     }
}

/*
 * Process receipts and acknowledgements using recnum as the receipt.
 */
receipt(conn, acknum, recnum)
register struct connection *conn;
unsigned short acknum, recnum;
{
     register struct packet *pkt, *pktl;

     ch_busy++;
     /*
      * Process a receipt, freeing packets that we now know have been
      * received.
      */
     if (cmp_gt(recnum, conn->cn_trecvd)) {
          for (pktl = conn->cn_thead;
               pktl != NOPKT && cmp_le(pktl->pk_pkn, recnum);
               pktl = pkt) {
               pkt = pktl->next;
               free_packet(pktl);
          }
          if ((conn->cn_thead = pktl) == NOPKT)
               conn->cn_ttail = NOPKT;
          conn->cn_trecvd = recnum;
     }
     /*
      * If the acknowledgement is new, update our idea of the
      * latest acknowledged packet, and wakeup output that might be blocked
      * on a full transmit window.
      */
     if (cmp_gt(acknum, conn->cn_tacked))
          if (cmp_gt(acknum, conn->cn_tlast)) {
               debug(DABNOR, (printf("Invalid acknowledgment(%d,%d)\n",
                    acknum, conn->cn_tlast)));
          } else {
               conn->cn_tacked = acknum;
               chd_newout(conn);
          }
     --ch_busy;
}


#ifdef BRDBRIDGE

int Chbrdbridge = 1;
#define MAXBRD    8

#endif BRDBRIDGE

/*
 * Process a received BRD
 * The trickiness is that we must modify the bit map completely before
 * sending it to anyone - thus we must remember all those to send it to.
 */

rcvbrd(pkt)
     register struct packet   *pkt;
{
     int       bitlen = pkt->pk_ackn;

          /* Here pk_ackn is # bytes in subnet bitmap */
     if ((pkt->pk_len <= bitlen) || (bitlen >= CHNSUBNET/8)) {
          free_packet(pkt);
          return;
     }
     if (flatten(pkt))
          return;

#ifdef BRDBRIDGE
     if (Chbrdbridge) {
          register struct chroute  *r;
          register int   mask, i;
          struct packet  *npkt;
	  chaddr    addrs[MAXBRD], *adp;

          bitp = pkt->data->rptr;
          for (i = 0, r = Chroutetab; r < bitlen; i++)
               for (mask = 1; mask & 0377; mask <<= 1, r++)
                    if (r->rt_type == CHDIRECT && r->rt_cost < HIGH_COST &&
                              *bitp & mask && adp < &addrs[MAXBRD]) {
                         *bitp &= ~mask;
			 *adp++ = r->rt_path.ifp->my_addr;
                    }
          while (adp > addrs) {			 
               if ((npkt = new_packet()) == NOPKT)
                    return;
               npkt->ph = pkt->ph;
               append_packet(npkt, pkt->data->rptr, pkt->pk_len);
               npkt->pk_daddr = *--adp;
               npkt->pk_daddr.ch_host = 0;
	       npkt->pk_pkn = 1;
               sendctl(npkt);
          }
     }
#endif BRDBRIDGE

     pkt->pk_lenword = pkt->pk_len - bitlen;
     pkt->data->rptr += bitlen;
     pkt->pk_ackn = 0;
     rcvrfc(pkt);
}

/*
 * Process a received RFC
 */
rcvrfc(pkt)
     register struct packet   *pkt;
{
     register struct connection    *conn, **conptr;
     register struct service  *svp;
     extern struct service    Chservice[];
     struct packet  **opkt, *pktl;

     debug(DPKT,prpkt(pkt,"RFC/BRD"));
     /*
      * Check if this is a duplicate RFC, and if so throw it away,
      * and retransmit the OPEN.
      */
     for (conptr = &Chconn[0]; conptr < &Chconn[NCH];)
          if ((conn = *conptr++) != NOCONN &&
                    conn->cn_fidx.ci_idx == pkt->pk_sidx.ci_idx &&
                     conn->cn_faddr.ch_addr == pkt->pk_saddr.ch_addr) {
               if (conn->cn_state == CSOPEN) {
                    debug(DPKT|DABNOR,
                         printf("Rcvrfc: Retransmitting open chan #%x\n",
			           (*conptr)->cn_lidx.ci_idx));
                    chretran(conn, 0);
               } else {
                    debug(DPKT|DABNOR,
                         printf("Rcvrfc: Duplicate RFC: %x\n",
                              conn->cn_lidx.ci_idx));
               }
               free_packet(pkt);
               return;
          }

     if (flatten(pkt)) {
          free_packet(pkt);
	  return;
     }

     /*
      * Scan the listen list for a listener and if one is found
      * open the connection and remove the listen packet from the
      * listen list.
      */
     for (opkt = &Chlsnlist; (pktl = *opkt) != NOPKT; opkt = &pktl->next)
          if(concmp(pkt, pktl->data->rptr, (int)pktl->pk_len)) {
               conn = Chconn[pktl->pk_sidx.ci_tidx];
               *opkt = pktl->next;
               free_packet(pktl);
               lsnmatch(pkt, conn);
               chd_newstate(conn);
               return;
          }
     /*
      * Check for built-in services.
      */
     for (svp = Chservice; svp->name != 0; ++svp)
          if (concmp(pkt, svp->name, svp->len)) {
	       (*svp->func)(pkt);
	       return;
          }
     /*
      * There was no listener, so queue the RFC on the unmatched RFC list
      * again checking for duplicates.
      */
     if ((pktl = Chrfclist) == NOPKT) 
          Chrfclist = Chrfctail = pkt;
     else {
          do {
               if(pktl->pk_sidx.ci_idx == pkt->pk_sidx.ci_idx &&
                  pktl->pk_saddr.ch_addr == pkt->pk_saddr.ch_addr) {
                    debug(DPKT/*|DABNOR*/,printf("Rcvrfc: Discarding duplicate Rfc on Chrfclist\n"));
                    free_packet(pkt);
                    return;
               }
          } while ((pktl = pktl->next) != NOPKT);
          Chrfctail->next = pkt;
          Chrfctail = pkt;
     }
     debug(DCONN,printf("Rcvrfc: Queued Rfc on Chrfclist\n"));
     /*
      * A new unmatched RFC has been received.  Send a copy of it up to the
      * unmatched RFC server.
      */
     copy_pkdata(ChaosQ, pkt);
}

/*
 * An RFC has matched a listener, either by an RFC coming and finding a match
 * on the listener list, or by a listen being done and matching an RFC on the
 * unmatched RFC list. So we change the state of the connection to CSRFCRCVD
 */
lsnmatch(rfcpkt, conn)
     register struct packet        *rfcpkt;
     register struct connection    *conn;
{
     debug(DCONN,printf("Conn #%x: LISTEN matched \n", conn->cn_lidx));
     /*
      * Initialize the conection
      */
     conn->cn_time = Chclock;
     conn->cn_state = CSRFCRCVD;
     if (conn->cn_rwsize == 0)
          conn->cn_rwsize = CHDRWSIZE;
     conn->cn_faddr = rfcpkt->pk_saddr;
     conn->cn_fidx = rfcpkt->pk_sidx;

     /*
      * Indicate to the other end that we have received and "read"
      * the RFC so that the open will truly acknowledge it.
      */
     conn->cn_rlast = conn->cn_rread = rfcpkt->pk_pkn;
     /*
      * Queue up the RFC for the user to read if he wants it.
      */
     chdrint(conn, rfcpkt);
}

/*
 * Remove a listener from the listener list, due to the listener bailing out.
 * Called from top level at high priority
 */
rmlisten(conn)
     register struct connection    *conn;
{
     register struct packet   *opkt, *pkt;

     opkt = NOPKT;
     for (pkt = Chlsnlist; pkt != NOPKT; opkt = pkt, pkt = pkt->next)
          if (pkt->pk_sidx.ci_tidx == conn->cn_lidx.ci_tidx) {
               if(opkt == NOPKT)
                    Chlsnlist = pkt->next;
               else
                    opkt->next = pkt->next;
               free_packet(pkt);
               break;
          }
}

/*
 * Compare the RFC contact name with the listener name.
 */
concmp(rfcpkt, lsnstr, lsnlen)
     struct packet  *rfcpkt;
     register char  *lsnstr;
     register int   lsnlen;
{
     register char *rfcstr = (char *)rfcpkt->data->rptr;
     register int rfclen;

     debug(DPKT,{printf("Rcvrfc: Comparing ");
                 print_str(rfcpkt->pk_len, rfcstr);
		 printf(" and ");
		 print_str(lsnlen, lsnstr);
		 printf("\n");});
     for (rfclen = rfcpkt->pk_len; rfclen; rfclen--, lsnlen--)
          if (lsnlen <= 0)
               return ((*rfcstr == ' ') ? 1 : 0);
          else if (*rfcstr++ != *lsnstr++)
               return(0);
     return (lsnlen == 0);
}


ch_input(conn, pkt)
     struct connection   *conn;
     struct packet       *pkt;
{
     int       pkn, op, controlled;

     pkn = pkt->pk_pkn;
     op = pkt->pk_op;
     controlled = CONTROLLED(pkt);

     chdrint(conn, pkt);

     if (controlled) {
          conn->cn_rread = pkn;
	  if (op == EOFOP)
	       sendsts(conn);
     }
}


#ifdef DEBUG
prpkt(pkt, str)
register struct packet *pkt;
char *str;
{
     printf("op=%s(%o) len=%d fc=%d\ndhost=%o didx=%x\nshost=%o sidx=%x\npkn=%d ackn=%d\n",
          str, pkt->pk_op, pkt->pk_len, pkt->pk_fc, pkt->pk_daddr.ch_addr,
          pkt->pk_didx.ci_idx, pkt->pk_saddr.ch_addr, pkt->pk_sidx.ci_idx,
          (unsigned)pkt->pk_pkn, (unsigned)pkt->pk_ackn);
}

print_str(len, s)
     register int   len;
     register char  *s;
{
     while (len-- > 0 && *s != '\0')
          printf("%c", *s++);
}
#endif DEBUG

#endif