BBN-V6/tcp/psipout.c

#
# include "tcpstru.h"
# include "hdrmasks.h"

# define ENDARGS        -1


PsipOut(WrkPtr)

struct WorkBlk *WrkPtr;

{
   extern char FlkBuff[TCPOTSZ];
   extern int FlkFlag;
   extern int Auth;
   extern int Debug;
   extern struct FdsBlk FdsBffer[FDSSIZE];
   extern char TcpOut[TCPOTSZ];
   extern int SegData;
   extern int PartWr;
   extern int NetFdsW;
   extern char *OutPtr;
   extern int OutCnt;
   extern ANLngth;

   register struct TcpTcb *XPtr;
   register char *DPtr;
   register char *SPtr;

   int DataSent;
   char *FPtr;
   int RemData;
   char *EndPtr;
   int i,j;
   int R1;
   int Beyond;
   struct FdsBlk *FdsBffPtr;
   int SecChng;
   int SegSec;
   int Count;

   struct
   {  int ADat;
    } *APtr;


   if (Debug == 1)
   {  printf("PsipOut: entered\n");
    }

   /* check if part of previous segment needs to be written */

   if (PartWr == 1)
   {  /* still have some of buffer left to export */
      Count = write(NetFdsW, OutPtr, OutCnt);
      if (Count == -1)
      {  printf("PsipOut: write error\n");
         return(1);     /* leave on work queue */
       }

      if (Count != OutCnt)
      {  OutPtr =+ Count;
         OutCnt =- Count;
         if (Debug == 4)
         {  Log(4, "s", "Part write", -1);
          }
         return(1);     /* leave on work queue */
       }

      PartWr = 0;
    }

   XPtr = WrkPtr -> TCBPtr;

   if (XPtr == &FlkFlag)
   {  /* send duplicate or out of order segment */
      /* copy segment to TcpOut buffer because
      of partial write problem with raw message interface */

      SPtr = &FlkBuff[BSLLNGTH - ANLngth*2 - 2];
      DPtr = &TcpOut[BSLLNGTH - ANLngth*2 - 2];

      APtr = SPtr;

      OutPtr = DPtr;
      OutCnt = APtr -> ADat;

      for (i = 0; i < OutCnt; i++)
      {  *DPtr++ = *SPtr++;
       }

      FlkFlag = 0;

      Count = write(NetFdsW, OutPtr, OutCnt);

      if (Count == -1)
      {  printf("PsipOut: write error\n");
         PartWr = 1;
         return(1);    /* leave on work queue */
       }

      if (Count != OutCnt)
      {  OutPtr =+ Count;
         OutCnt =- Count;
         PartWr = 1;
         return(1);    /* leave on work queue */
       }
      return(0);
    }

   if (((XPtr -> Flags)&040) > 0)
   {  /* RST received - send no more segments */
      return(0);    /* remove work block */
    }

   /* send out a segment */

   XPtr -> SeqNo = XPtr -> SendSeq;

   i = 0;   /* byte counter */

   Beyond = 0;    /* flag: 1 => sending beyond right edge */

   /* calculate remaining data to be sent */
   RemData = (XPtr -> DataTl) - (XPtr -> DataNxt);

   if (RemData < 0)
   {  RemData =+ RBUFFSIZE;
    }

   /* check if SYN needs to be sent */
   if (XPtr -> SeqNo == XPtr -> SndISN)
   {  /* first sequence number */
      XPtr -> TCPFlags =| SYN;    /* set SYN bit */
      XPtr -> SendSeq =+ 1;

      if (XPtr -> State == OPEN)
      {  XPtr -> State = SYNSENT;
       }
    }

   /* check for urgent state */
   if (((XPtr -> Flags)&01) > 0)
   {  /* urgent condition flag on */
      /* check if urgent number is beyond next to send */

      R1 = ULCompare(&(XPtr -> SndUrgNo), &(XPtr -> SeqNo));

      if (R1 != 2)
      /* SndUrgNo >= SendSeq */
      {  /* put on the urgent bit */
         XPtr -> UrgentPtr = (XPtr -> SndUrgNo) - (XPtr -> SeqNo);
         XPtr -> TCPFlags =| URG;    /* URG <- 1 */
       }
    }

   DataSent = (XPtr -> DataNxt) - (XPtr -> DataHd);

   if (DataSent < 0)
   {  DataSent =+ RBUFFSIZE;
    }

   if (RemData != 0)
   {
      DPtr = &TcpOut[BSLLNGTH + INLNGTH + TCPLNGTH];
      SPtr = &(XPtr -> RetranBuff[XPtr -> DataNxt]);
      FPtr = &(XPtr -> FlagBuff[XPtr -> DataNxt]);
      EndPtr = &(XPtr -> RetranBuff[RBUFFSIZE]);

      /* pick up the security level */
      SegSec = (*FPtr) >> 3;

      /* check if being held up by window limits */
      if ((XPtr -> SndWindow == 0)||
      ((((XPtr -> TCPFlags)&URG) > 0)&&(DataSent >= XPtr -> SndWindow)))

      {  /* check if this is the first segment into a zero window or
         that this segment is a retransmission */

         if ((((XPtr -> Flags)&XRTNDATA) == 0)||
         (((XPtr -> Flags)&(XRTNDATA+XRETRANOK)) == (XRTNDATA+XRETRANOK)))
         {  /* send at least one byte */
            *DPtr = *SPtr;
            i = 1;
            /* prohibit data sent until retransmission time */
            /* this could occur with an ACK being sent */

            XPtr -> Flags =| XRTNDATA;
          }

         /* ensure that no more data is sent until retransmission */
         Beyond = 1;
       }
      else
      {
         SecChng = 0;
         while ( ((i + DataSent) < (XPtr -> SndWindow))&&
         (i < RemData)&&( i < SegData)&&(SecChng == 0) )
         {  *DPtr++ = *SPtr++;
            i++;
            if (SPtr == EndPtr)
            {  SPtr = &(XPtr -> RetranBuff[0]);
               FPtr = &(XPtr -> FlagBuff[0]);
             }
            if (Auth == 1)
            {  if (((*FPtr) >> 3) == SegSec)
               {  FPtr++;
                }
               else
               {  SecChng = 1;
                }
             }
          }
       }
    }

   /* reset the retran flag */
   XPtr -> Flags =& ~XRETRANOK;

   /* check if time to send FIN */
   if ((((XPtr -> Flags)&010) > 0)&&
   (XPtr -> SndFSN == XPtr -> SendSeq))
   {  /* no more data will be sent */
      XPtr -> TCPFlags =| FIN;    /* FIN <- 1 */
    }

   if ((i == 0)&&(((XPtr -> TCPFlags)&0377) == 0))
   {  /* nothing to send */
      if (RemData > 0)
      {  /* still have data though */
         return(1);
       }
      return(0);
    }

   /* set EOL bit always for the tenex guys */

   XPtr -> TCPFlags =| EOL;

   XPtr -> SegLength = i + INLNGTH + TCPLNGTH;
   /* always set ACK bit if connection is not SYNSENT */
   switch(XPtr -> State)
   {  case SYNRECD:
      case ESTAB:
      case CLOSEWAIT:
      case FINWAIT:
      case CLOSING:
      {  /* set ACK bit */
         XPtr -> TCPFlags =| ACK;
       }
    }

   /* set the security level */
   if (i == 0)   /* check for data */
   {  XPtr -> Options[2] = XPtr -> ScMinOut;
    }
   else
   {  XPtr -> Options[2] = SegSec;
    }

   /* set precedence level */
   XPtr -> Options[2] =| (XPtr -> SndPrec) << 4;

   /* set the TCC */
   XPtr -> Options[3] = XPtr -> TccList[0];

   /* copy leaders to output buffer */

   DPtr = TcpOut;
   SPtr = &(XPtr -> PrecSec);

   for (j = 0; j < (BSLLNGTH + INLNGTH + TCPLNGTH); j++)
   {  *DPtr++ = *SPtr++;
    }

   /* write out the segment to the net */

   NetOut(0);

   /* update the state information */
   XPtr -> DataNxt =+ i;

   if ((XPtr -> DataNxt) >= RBUFFSIZE)
   {  XPtr -> DataNxt =- RBUFFSIZE;
    }

   XPtr -> SendSeq =+ i;

   if (Debug == 1)
   {  /* snap the segment info */
      printf("PsipOut: segment info:  L = %d, F = %o, Src = %d\n", i,
      (XPtr -> TCPFlags)&07777, XPtr -> SrcePort);
    }

   if ((((XPtr -> TCPFlags)&RST) > 0)&&(((XPtr -> Flags)&020) > 0))
   {  /* the RST was sent, now clean up the connection */
      return(3);    /* make the scheduler abort the connection */
    }

   /* check for state change */
   if (((XPtr -> TCPFlags)&FIN) > 0)
   {  XPtr -> SendSeq =+ 1;

      switch(XPtr -> State)
      {  case SYNRECD:
         case ESTAB:
         {  XPtr -> State = FINWAIT;
            break;
          }
         case CLOSEWAIT:
         {  XPtr -> State = CLOSING;
            break;
          }
       }
    }

   /* update MaxSend if not retransmitting */
   R1 = ULCompare(&(XPtr -> SendSeq),&(XPtr -> MaxSend));

   if (R1 == 1)
   /* SendSeq > MaxSend */
   {  XPtr -> MaxSend = XPtr -> SendSeq;
    }

   if ((i > 0)||(((XPtr -> TCPFlags)&(SYN+FIN)) > 0))
   {  /* put TCB on retransmission queue */
      QTcb(XPtr);
    }

   /* clear the control bits */
   XPtr -> TCPFlags =& ~07777;

   /* if window not closed,
   move work block to end of queue if
   there is more data to segmentize */
   if ((Beyond != 1)&&((RemData - i) > 0))
   {
      return(2);   /* move to end of queue */
    }
   if ((Beyond == 1)&&(PartWr == 1))
   {  printf("B = 1 and PWr = 1\n");
    }

   if (PartWr == 1)
   {  return(1);      /* leave on queue */
    }

   return(0);   /* delete work entry */
 }



NetOut(EchoSeg)

int EchoSeg;

{
   extern int ShortCkt;
   extern int ShortIn;
   extern int PartWr;
   extern char *OutPtr;
   extern int OutCnt;
   extern int *TimePtr;
   extern int RTime;
   extern int FlkFlag;
   extern char FlkBuff[TCPOTSZ];
   extern int SegNum;
   extern int ANLngth;
   extern int Debug;
   extern int InLink;
   extern int LocHost;
   extern int LocImp;
   extern int LocNet;
   extern int NetFdsW;
   extern int NetFdsR;
   extern char TcpOut[TCPOTSZ];
   extern char TcpIn[TCPINSZ];
   extern char NumLost;
   extern char NumBad;
   extern char NumDup;
   extern char TimeDup;
   extern char NumReOrd;
   extern char TimeReOrd;
   extern int NetTable[NUMNET];
   extern int DoChkSum;

   register char *SPtr, *DPtr;

   struct ANMask1 *ANPtr1;
   struct ANMask2 *ANPtr2;
   struct INLMask *INPtr;
   struct TCPMask *TCPPtr;

   int Error;
   int ILength;
   int DLength;
   int TLength;
   int Count;
   char *BPtr;
   int Chk;
   int Echo;
   int PHdr[2];
   int i;

   INPtr = &TcpOut[BSLLNGTH];
   ILength = ((INPtr -> IVerHdr)&017)*4;

   if (ShortCkt == 1)
   {  /* check if this segment is destined for this host */

      if ((INPtr -> IDstNet == LocNet)&&
      (INPtr -> IDHstH == LocHost)&&
      (((INPtr -> IDHstL)&0377) == LocImp))
      {  /* no real need to send to the net */

         /* set the segment ID */
         INPtr -> ISegID = SegNum;
         SegNum =+ 1;

         /* copy TcpOut buffer to TcpIn */

         SPtr = &TcpOut[BSLLNGTH];
         DPtr = &TcpIn[BSLLNGTH];
         Count = INPtr -> ISegLength;

         for (i = 0; i < Count; i++)
         {  *DPtr++ = *SPtr++;
          }

         ShortIn = 1;    /* set flag for PsipIn */

         /* make work for PsipIn */

         MakeWrk(0, NETINHP, 0, NetFdsR);

         return(0);
       }
    }

   ANPtr1 = &TcpOut[BSLLNGTH - ANLngth*2 - 2];
   ANPtr2 = ANPtr1;

   /* set the length regardless of AN leader size */
   ANPtr1 -> MLngth1 = ANLngth*2 + 2 + INPtr -> ISegLength;

   OutPtr = ANPtr1;
   OutCnt = ANPtr1 -> MLngth1;

   /* determine destination host */

   if (INPtr -> IDstNet >= NUMNET)
   {  i = NetTable[0];  /* send to smart gateway */
    }
   else
   {  i = NetTable[INPtr -> IDstNet];
    }

   if (i == -1)
   {  /* don't use gateway */
      i = ((INPtr -> IDHstL)&0377) + ((INPtr -> IDHstH) << 8);
    }

   if (ANLngth == 2)
   {  ANPtr1 -> Type1 = 0;
      ANPtr1 -> Link1 = InLink;    /* internet link number */
      ANPtr1 -> SbType1 = 0;
      ANPtr1 -> Host1 = (i&077) + ((i&01400) >> 2);
    }
   else
   {  ANPtr2 -> Fmt2 = 15;
      ANPtr2 -> SNet2 = 0;
      ANPtr2 -> LdrFlgs2 = 0;
      ANPtr2 -> Type2 = 0;
      ANPtr2 -> HandType2 = 0;
      ANPtr2 -> LogHost2 = 0;
      ANPtr2 -> Link2 = InLink;
      ANPtr2 -> SbType2 = 0;
      ANPtr2 -> Host2 = i >> 8;
      ANPtr2 -> Imp2 = i&0377;

      /* the next value may eventually have to be real */
      ANPtr2 -> ALngth2 = 0;
    }

   if (EchoSeg == 0)
   {  /* set the segment ID */
      INPtr -> ISegID = SegNum;
      SegNum =+ 1;

      TCPPtr = &TcpOut[BSLLNGTH + ILength];
      TLength = ((TCPPtr -> TTCPFlags) >> 12)*4;

      DLength = INPtr -> ISegLength - ILength - TLength;

      if (DoChkSum == 1)
      {  /* compute internet checksum */
         INPtr -> IINChkSum = ~ChkSum(INPtr, ILength/2, 0, 0);

         /* compute TCP checksum */
         /* point to internet source address */

         BPtr = &TcpOut[BSLLNGTH+12];

         Chk = ChkSum(BPtr, 4,0 ,0);

         BPtr = &PHdr[0];
         PHdr[0] = (INPtr -> ISegLength) - ILength;
         PHdr[1] = INPtr -> IProtocol;

         Chk = ChkSum(BPtr, 2, Chk, 0);

         BPtr = TCPPtr;

         Chk = ChkSum(BPtr, TLength/2, Chk, 0);

         BPtr = &TcpOut[BSLLNGTH + ILength + TLength];

         /* check for odd number of bytes */
         if ((DLength&01) > 0)
         {  /* need to pad with a zero byte */

            TcpOut[BSLLNGTH + ILength + TLength + DLength] = 0;
            DLength =+ 1;
          }

         TCPPtr -> TTCPChkSum = ~ChkSum(BPtr, DLength/2, Chk, 1);
       }
    }

   if ((Debug == 1)||(Debug == 2)||(Debug == 4))
   {
      Error = Log(1, "n", INPtr, ENDARGS);
      if (EchoSeg == 0)
      {  Error = Log(1, "t", TCPPtr, ENDARGS);
       }
    }

   /* swap bytes before sending */

   Swap(INPtr, ILength);

   if (EchoSeg == 0)
   {  Swap(TCPPtr, TLength);
    }

   /* flakiness simulation */

   /* do we need to lose the segment? */
   if (NumLost > 0)
   {  if (rand()%100 < NumLost)
      {  /* discard the segment */
         printf("Lost %d\n", SegNum-1);
         return(ANPtr1 -> MLngth1);  /* don't write to net */
       }
    }

   /* do we need to smash the segment? */
   if (NumBad > 0)
   {  if (rand()%100 < NumBad)
      {  i = BSLLNGTH + rand()%40;   /* 40 => in TCP/IN hdrs */
         TcpOut[i] = ~TcpOut[i];   /* some random place */
         printf("Smashed %d B: %d\n", SegNum-1, i);
       }
    }

   /* do we need to make a delayed duplicate? */
   if ((NumDup > 0)&&(FlkFlag == 0))
   {  if ((rand()%100) < NumDup)
      {  /* copy TcpOut to FlkBuff */

         SPtr = &TcpOut[BSLLNGTH - ANLngth*2 - 2];
         DPtr = &FlkBuff[BSLLNGTH - ANLngth*2 - 2];

         Count = ANPtr1 -> MLngth1;

         for (i = 0; i < Count; i++)
         {  *DPtr++ = *SPtr++;
          }

         RTime = (rand()%TimeDup) + *TimePtr;

         /* set flag to allow only one duplicate */
         FlkFlag = 1;

         printf("Duped %d T: %d\n", SegNum-1, RTime - *TimePtr);
       }
    }

   /* do we need to reorder the segment? */
   if ((NumReOrd > 0)&&(FlkFlag == 0))
   {  if ((rand()%100) < NumReOrd)
      {  /* copy TcpOut to FlkBuff */

         SPtr = &TcpOut[BSLLNGTH - ANLngth*2 - 2];
         DPtr = &FlkBuff[BSLLNGTH - ANLngth*2 - 2];

         Count = ANPtr1 -> MLngth1;

         for (i = 0; i < Count; i++)
         {  *DPtr++ = *SPtr++;
          }

         RTime = (rand()%TimeReOrd) + *TimePtr;

         /* set flag to allow only one duplicate */
         FlkFlag = 1;

         printf("Reordered %d T: %d\n", SegNum-1, RTime - *TimePtr);

         return(ANPtr1 -> MLngth1);  /* don't write to net */
       }
    }

   /* write the segment to the net */

   Count = write(NetFdsW, ANPtr1, ANPtr1 -> MLngth1);

   if (Count == -1)
   {  /* write error */
      printf("NetOut: write error\n");
      return(0);
    }

   if (Count != OutCnt)
   {  PartWr = 1;  /* need to finish sending this segment */
      OutPtr =+ Count;
      OutCnt =- Count;
      if (Debug == 4)
      {  Log(3, "s", "Part write", -1);
       }
    }

   return(0);
 }