home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff225.lzh / AmigaTCP / src / tcpout.c < prev    next >
C/C++ Source or Header  |  1989-06-24  |  6KB  |  205 lines

  1. #include "machdep.h"
  2. #include "timer.h"
  3. #include "mbuf.h"
  4. #include "netuser.h"
  5. #include "internet.h"
  6. #include "tcp.h"
  7.  
  8. int16 tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */
  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;
  18.     struct mbuf *hbp;
  19.     int16 hsize;    /* Size of header */
  20.     struct tcp_header *tcph;
  21.     struct mss *mssp;
  22.     int16 ssize;    /* Size of current segment being sent,
  23.              * including SYN and FIN flags */
  24.     int16 dsize;    /* Size of segment less SYN and FIN */
  25.     int16 usable;    /* Usable window */
  26.     int16 sent;    /* Sequence count (incl SYN/FIN) already in the pipe */
  27.  
  28.     if(tcb == NULLTCB)
  29.         return;
  30.  
  31.     switch(tcb->state){
  32.     case LISTEN:
  33.     case CLOSED:
  34.         return;    /* Don't send anything */
  35.     }
  36.     for(;;){
  37.         sent = tcb->snd.ptr - tcb->snd.una;
  38.  
  39.         /* If this is a retransmission, send only the oldest segment
  40.          * (first-only retransmission policy)
  41.          */
  42.         if(tcb->retry != 0 && sent != 0)
  43.             break;
  44.  
  45.         /* There can only be one outstanding segment in this state
  46.          * since the other end would reject any segment without SYN
  47.          */
  48.         if(tcb->state == SYN_SENT && sent != 0)
  49.             break;
  50.         if(tcb->snd.wnd == 0){
  51.             /* Allow only one closed-window probe at a time */
  52.             if(sent != 0)
  53.                 break;
  54.             /* Force a closed-window probe */
  55.             usable = 1;
  56.         } else {
  57.             /* usable window = offered window - unacked bytes in transit */
  58.             usable = tcb->snd.wnd - sent;
  59.  
  60.             /* John Nagle's "single outstanding segment" rule.
  61.              * Allow only one segment in the pipeline unless there is enough
  62.              * unsent data to form at least one maximum-sized segment.
  63.              */
  64.             if(sent != 0 && tcb->sndcnt - sent < tcb->mss){
  65.                 usable = 0;
  66.             }
  67.             /* Silly window avoidance. Don't send anything if the usable window
  68.              * is less than a quarter of the offered window.
  69.              * This test comes into play only when the offered window is at
  70.              * least 4 times the MSS; otherwise Nagle's test is sufficient
  71.              * to prevent SWS.
  72.               */
  73.             else if(usable < tcb->snd.wnd/4){
  74.                 usable = 0;
  75.             }
  76.         }
  77.         /* Compute size of segment to send. This is either the usable
  78.          * window, the mss, or the amount we have on hand, whichever is less.
  79.          * (I don't like optimistic windows)
  80.          */
  81.         ssize = min(tcb->sndcnt - sent,usable);
  82.         ssize = min(ssize,tcb->mss);
  83.         dsize = ssize;
  84.  
  85.         if(ssize == 0 && tcb->force == 0)
  86.             break;        /* No need to send anything */
  87.  
  88.         /* Determine size of TCP header and allocate mbuf.
  89.          * If sending SYN, allow space for the MSS option
  90.          */
  91.         switch(tcb->state){
  92.         case SYN_SENT:
  93.         case SYN_RECEIVED:
  94.             hsize = sizeof(struct tcp_header) + sizeof(struct mss);
  95.             break;
  96.         default:
  97.             hsize = sizeof(struct tcp_header);
  98.             break;
  99.         }
  100.         if((hbp = alloc_mbuf(hsize)) == NULLBUF)
  101.             break;    /* No room to form a packet */
  102.  
  103.         tcb->force = 0;    /* Only one forced segment! */
  104.         hbp->cnt = hsize;
  105.  
  106.         tcph = (struct tcp_header *)hbp->data;
  107.         tcph->source = htons(tcb->conn.local.port);
  108.         tcph->dest = htons(tcb->conn.remote.port);
  109.         tcph->offset = hsize/sizeof(int32) << DSHIFT;
  110.         tcph->flags = 0;
  111.  
  112.         /* Set the SYN and ACK flags according to the state we're in. It is
  113.          * assumed that if this segment is associated with a state transition,
  114.          * then the state change will already have been made. This allows
  115.          * this routine to be called from a retransmission timeout with
  116.          * force=1.
  117.          * If SYN is being sent, adjust the dsize counter so we'll
  118.          * try to get the right amount of data off the send queue.
  119.          */
  120.         switch(tcb->state){
  121.         case SYN_SENT:
  122.         case SYN_RECEIVED:
  123.             if(tcb->snd.ptr == tcb->iss){
  124.                 tcph->flags = SYN;
  125.                 dsize--;
  126.             }
  127.             /* Also send MSS */
  128.             mssp = (struct mss *)(tcph + 1);
  129.             mssp->kind = MSS_KIND;
  130.             mssp->length = MSS_LENGTH;
  131.             mssp->mss = htons(tcp_mss);
  132.         }
  133.         switch(tcb->state){
  134.         case SYN_RECEIVED:
  135.         case ESTABLISHED:
  136.         case CLOSE_WAIT:
  137.         case FINWAIT2:
  138.         case TIME_WAIT:
  139.         case CLOSING:
  140.         case FINWAIT1:
  141.         case LAST_ACK:
  142.             tcph->flags |= ACK;
  143.             break;
  144.         }
  145.         tcph->seq = htonl(tcb->snd.ptr);
  146.         tcph->ack = htonl(tcb->rcv.nxt);
  147.         tcph->wnd = htons(tcb->rcv.wnd);
  148.         tcph->checksum = 0;
  149.         tcph->up = 0;
  150.  
  151.         /* Now try to extract some data from the send queue.
  152.          * Since SYN and FIN occupy sequence space and are reflected
  153.          * in sndcnt but don't actually sit in the send queue,
  154.          * dup_p will return one less than dsize if a FIN needs to be sent.
  155.          */
  156.         if(dsize != 0){
  157.             if(dup_p(&hbp->next,tcb->sndq,sent,dsize) != dsize){
  158.                 /* We ran past the end of the send queue; send a FIN */
  159.                 tcph->flags |= FIN;
  160.                 dsize--;
  161.             }
  162.         }
  163.         /* If the entire send queue will now be in the pipe, set the
  164.          * push flag
  165.          */
  166.         if(dsize != 0 && sent + ssize == tcb->sndcnt)
  167.             tcph->flags |= PSH;
  168.  
  169.         tcb->snd.ptr += ssize;
  170.         /* If this is the first transmission of a range of sequence
  171.          * numbers, record it so we'll accept acknowledgments
  172.          * for it later
  173.          */
  174.         if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
  175.             tcb->snd.nxt = tcb->snd.ptr;
  176.         /* Fill in fields of pseudo IP header */
  177.         ph.source = tcb->conn.local.address;
  178.         ph.dest = tcb->conn.remote.address;
  179.         ph.protocol = TCP_PTCL;
  180.         ph.zero = 0;
  181.         ph.length = hsize + dsize;
  182.  
  183.         /* Compute checksum over pseudo-header, TCP header and data,
  184.          * and pass it off to IP
  185.          */
  186.         tcph->checksum = cksum(&ph,hbp,ph.length);
  187.  
  188.         /* If we're sending some data or flags, start retransmission
  189.          * timer if it isn't already running.
  190.          */
  191.         if(ssize != 0){
  192.             if(tcb->timer.state != TIMER_RUN){
  193.                 /* Never initialize the timer with zero; it won't run! */
  194.                 tcb->timer.start = max(tcb->timer.start,1);
  195.                 start_timer(&tcb->timer);
  196.             }
  197.             /* If round trip timer isn't running, start it */
  198.             if(seq_ge(tcb->snd.una,tcb->rttseq))
  199.                 tcb->rttseq = tcb->snd.ptr;
  200.         }
  201.         ip_send(tcb->conn.local.address,tcb->conn.remote.address,
  202.          TCP_PTCL,tcb->tos,0,hbp,ph.length,0,0);
  203.     }
  204. }
  205.