home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / telecomm / nhclb120 / tcpout.c < prev    next >
C/C++ Source or Header  |  1993-09-26  |  6KB  |  201 lines

  1. #include "global.h"
  2. #include "timer.h"
  3. #include "mbuf.h"
  4. #include "netuser.h"
  5. #include "internet.h"
  6. #include "tcp.h"
  7.  
  8. int last_retran;
  9.  
  10. /* Send a segment on the specified connection. One gets sent only
  11.  * if there is data to be sent or if "force" is non zero
  12.  */
  13. void
  14. tcp_output(tcb)
  15. register struct tcb *tcb;
  16. {
  17.     struct pseudo_header ph;/* Pseudo-header for checksum calcs */
  18.     struct mbuf *hbp,*dbp;    /* Header and data buffer pointers */
  19.     int16 hsize;        /* Size of header */
  20.     struct tcp seg;        /* Local working copy of header */
  21.     int16 ssize;        /* Size of current segment being sent,
  22.                  * including SYN and FIN flags */
  23.     int16 dsize;        /* Size of segment less SYN and FIN */
  24.     int16 usable;        /* Usable window */
  25.     int16 sent;        /* Sequence count (incl SYN/FIN) already in the pipe */
  26.  
  27.     if(tcb == NULLTCB)
  28.         return;
  29.  
  30.     switch(tcb->state){
  31.     case LISTEN:
  32.     case CLOSED:
  33.         return;    /* Don't send anything */
  34.     }
  35.     for(;;){
  36.         sent = tcb->snd.ptr - tcb->snd.una;
  37.  
  38. #ifdef    notdef
  39.         /* If this is a retransmission, send only the oldest segment
  40.          * (first-only retransmission policy) -- OBSOLETED by cwind
  41.          */
  42.         if((tcb->flags & RETRAN) && sent != 0)
  43.             break;
  44. #endif
  45.         /* Don't send anything else until our SYN has been acked */
  46.         if(sent != 0 && !(tcb->flags & SYNACK))
  47.             break;
  48.  
  49.         if(tcb->snd.wnd == 0){
  50.             /* Allow only one closed-window probe at a time */
  51.             if(sent != 0)
  52.                 break;
  53.             /* Force a closed-window probe */
  54.             usable = 1;
  55.         } else {
  56.             /* usable window = offered window - unacked bytes in transit
  57.              * limited by the congestion window
  58.              */
  59.             usable = min(tcb->snd.wnd,tcb->cwind) - sent;
  60.  
  61.             /* John Nagle's "single outstanding segment" rule.
  62.              * Allow only one segment in the pipeline unless there is enough
  63.              * unsent data to form at least one maximum-sized segment.
  64.              */
  65.             if(sent != 0 && tcb->sndcnt - sent < tcb->mss){
  66.                 usable = 0;
  67.             }
  68. #ifdef    notdef
  69.             /* Silly window avoidance. Don't send anything if the usable window
  70.              * is less than a quarter of the offered window.
  71.              * This test comes into play only when the offered window is at
  72.              * least 4 times the MSS; otherwise Nagle's test is sufficient
  73.              * to prevent SWS.
  74.               */
  75.             else if(usable < tcb->snd.wnd/4){
  76.                 usable = 0;
  77.             }
  78. #endif
  79.         }
  80.         /* Compute size of segment to send. This is either the usable
  81.          * window, the mss, or the amount we have on hand, whichever is less.
  82.          * (I don't like optimistic windows)
  83.          */
  84.         ssize = min(tcb->sndcnt - sent,usable);
  85.         ssize = min(ssize,tcb->mss);
  86.         dsize = ssize;
  87.  
  88.         if(ssize == 0 && !(tcb->flags & FORCE))
  89.             break;        /* No need to send anything */
  90.  
  91.         tcb->flags &= ~FORCE;    /* Only one forced segment! */
  92.  
  93.         seg.source = tcb->conn.local.port;
  94.         seg.dest = tcb->conn.remote.port;
  95.  
  96.         /* Set the SYN and ACK flags according to the state we're in. It is
  97.          * assumed that if this segment is associated with a state transition,
  98.          * then the state change will already have been made. This allows
  99.          * this routine to be called from a retransmission timeout with
  100.          * force=1.
  101.          * If SYN is being sent, adjust the dsize counter so we'll
  102.          * try to get the right amount of data off the send queue.
  103.          */
  104.         seg.flags = ACK; /* Every state except SYN_SENT */
  105.         hsize = TCPLEN;    /* Except when SYN being sent */
  106.         seg.mss = 0;
  107.  
  108.         switch(tcb->state){
  109.         case SYN_SENT:
  110.             seg.flags = 0;    /* Note fall-thru */
  111.         case SYN_RECEIVED:
  112.             if(tcb->snd.ptr == tcb->iss){
  113.                 seg.flags |= SYN;
  114.                 dsize--;
  115.                 /* Also send MSS */
  116.                 seg.mss = tcp_mss;
  117.                 hsize = TCPLEN + MSS_LENGTH;
  118.             }
  119.             break;
  120.         }
  121.         seg.seq = tcb->snd.ptr;
  122.         seg.ack = tcb->rcv.nxt;
  123.         seg.wnd = tcb->rcv.wnd;
  124.         seg.up = 0;
  125.  
  126.         /* Now try to extract some data from the send queue.
  127.          * Since SYN and FIN occupy sequence space and are reflected
  128.          * in sndcnt but don't actually sit in the send queue,
  129.          * dup_p will return one less than dsize if a FIN needs to be sent.
  130.          */
  131.         if(dsize != 0){
  132.             if(dup_p(&dbp,tcb->sndq,sent,dsize) != dsize){
  133.                 /* We ran past the end of the send queue; send a FIN */
  134.                 seg.flags |= FIN;
  135.                 dsize--;
  136.             }
  137.         } else
  138.             dbp = NULLBUF;
  139.  
  140.         /* If the entire send queue will now be in the pipe, set the
  141.          * push flag
  142.          */
  143.         if(dsize != 0 && sent + ssize == tcb->sndcnt)
  144.             seg.flags |= PSH;
  145.  
  146.         /* If this transmission includes previously transmitted data,
  147.          * snd.nxt will already be past snd.ptr. In this case,
  148.          * compute the amount of retransmitted data and keep score
  149.          */
  150.         if(tcb->snd.ptr < tcb->snd.nxt)
  151.             tcb->resent += min(tcb->snd.nxt - tcb->snd.ptr,ssize);
  152.  
  153.         tcb->snd.ptr += ssize;
  154.         /* If this is the first transmission of a range of sequence
  155.          * numbers, record it so we'll accept acknowledgments
  156.          * for it later
  157.          */
  158.         if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
  159.             tcb->snd.nxt = tcb->snd.ptr;
  160.  
  161.         /* Fill in fields of pseudo IP header */
  162.         ph.source = tcb->conn.local.address;
  163.         ph.dest = tcb->conn.remote.address;
  164.         ph.protocol = TCP_PTCL;
  165.         ph.length = hsize + dsize;
  166.  
  167.         /* Generate TCP header, compute checksum, and link in data */
  168.         if((hbp = htontcp(&seg,dbp,&ph)) == NULLBUF){
  169.             free_p(dbp);
  170.             return;
  171.         }
  172.         /* If we're sending some data or flags, start retransmission
  173.          * and round trip timers if they aren't already running.
  174.          */
  175.         if(ssize != 0){
  176.             tcb->timer.start = backoff(tcb->backoff) *
  177.              (2 * tcb->mdev + tcb->srtt + MSPTICK
  178. #ifdef SYS5
  179. /* 
  180.  * The main loop has a select with a 100 msec timeout, so we give
  181.  * an extra 100 msec grace before timing out.  The rtt does not
  182.  * include the extra 100 msec, because incoming packets trigger
  183.  * the select.
  184.  */
  185.                             + 100
  186. #endif
  187.                                  ) / MSPTICK;
  188.             start_timer(&tcb->timer);
  189.  
  190.             /* If round trip timer isn't running, start it */
  191.             if(!run_timer(&tcb->rtt_timer)){
  192.                 start_timer(&tcb->rtt_timer);
  193.                 tcb->rttseq = tcb->snd.ptr;
  194.             }
  195.         }
  196.         last_retran = tcb->flags & RETRAN;
  197.         ip_send(tcb->conn.local.address,tcb->conn.remote.address,
  198.          TCP_PTCL,tcb->tos,0,hbp,ph.length,0,0);
  199.     }
  200. }
  201.