home *** CD-ROM | disk | FTP | other *** search
/ GRIPS 2: Government Rast…rocessing Software & Data / GRIPS_2.cdr / dos / ncsa_tel / tel_2_2_ / source / tcp.c < prev    next >
C/C++ Source or Header  |  1988-07-15  |  17KB  |  624 lines

  1. /*
  2. *   TCP routines
  3. *
  4. ****************************************************************************
  5. *                                                                          *
  6. *      part of:                                                            *
  7. *      TCP/UDP/ICMP/IP Network kernel for NCSA Telnet                      *
  8. *      by Tim Krauskopf                                                    *
  9. *                                                                          *
  10. *      National Center for Supercomputing Applications                     *
  11. *      152 Computing Applications Building                                 *
  12. *      605 E. Springfield Ave.                                             *
  13. *      Champaign, IL  61820                                                *
  14. *                                                                          *
  15. ****************************************************************************
  16. *   Tim Krauskopf          Fall 1986
  17. *    mods for int16/int32  2/88
  18. */
  19. #include "stdio.h"
  20. #include "protocol.h"
  21. #include "data.h"
  22.  
  23. static int pnum;
  24. /************************************************************************
  25. *  tcpinterpret
  26. *  This is called when a packet comes in, passes IP checksumming and is
  27. *  of the TCP protocol type.  Check and see if we are expecting it and
  28. *  where the data should go.
  29. */
  30. tcpinterpret(p,tlen)
  31.     int tlen;
  32.     TCPKT *p;
  33.     {
  34.     uint i,myport,hlen,hisport;
  35.     struct port *prt;
  36.     
  37. /*
  38. *  checksum
  39. *    First, fill the pseudo header with its fields, then run our
  40. *  checksum to confirm it.
  41. *
  42. */
  43.     if (p->t.check) {
  44.         movebytes(tcps.source,p->i.ipsource,8);  /* move both addresses */
  45.         tcps.z = 0;
  46.         tcps.proto = p->i.protocol;
  47.  
  48.         tcps.tcplen = intswap(tlen);            /* byte-swapped length */
  49.  
  50.         if (tcpcheck(&tcps,&p->t,tlen)) {    /* compute checksum */
  51.             netposterr(400);
  52.             return(2);
  53.         }
  54.     }
  55.  
  56. /*
  57. *  find the port which is associated with the incoming packet
  58. *  First try open connections, then try listeners
  59. */
  60.     myport = intswap(p->t.dest);
  61.     hisport = intswap(p->t.source);
  62.     hlen = p->t.hlen >> 2;                /* bytes offset to data */
  63.  
  64.  
  65.     for (i=0; i<NPORTS; i++) {
  66.  
  67.         prt = portlist[i];
  68.  
  69.         if (prt != NULL && prt->in.port == myport && prt->out.port == hisport) {
  70.             pnum = i;
  71.             return(tcpdo(prt,p,tlen,hlen));
  72.         }
  73.  
  74.     }
  75.  
  76. /*
  77. *  check to see if the incoming packet should go to a listener
  78. */
  79.  
  80.     for (i=0; i < NPORTS; i++) {
  81.         prt = portlist[i];
  82.  
  83.         if (prt != NULL && !prt->out.port && 
  84.             prt->in.port == myport && (p->t.flags & TSYN)) {
  85.             pnum = i;
  86.             return(tcpdo(prt,p,tlen,hlen));
  87.             }
  88.  
  89.     }
  90.  
  91. /*
  92. *  no matching port was found to handle this packet, reject it
  93. */
  94.  
  95.         tcpreset(p);                /* tell them they are crazy */
  96.         if (!(p->t.flags & TSYN))    /* no error message if it is a SYN */
  97.             netposterr(407);            /* invalid port for incoming packet */
  98.  
  99.         return(1);                    /* no port matches */
  100.  
  101. }
  102.  
  103. /**********************************************************************/
  104. /*  tcpdo
  105. *  Looking at the port structure for the destination port, deliver
  106. *  the incoming packet.
  107. */
  108. tcpdo(prt,p,tlen,hlen)
  109.     int tlen,hlen;
  110.     struct port *prt;
  111.     TCPKT *p;
  112.     {
  113.  
  114.     switch (prt->state) {
  115.  
  116.         case SLISTEN:                    /* waiting for remote connection */
  117.             if (p->t.flags & TSYN) {    /* receive SYN */
  118. /*
  119. *   remember anything important from the incoming TCP header 
  120. */
  121.                 prt->out.size = intswap(p->t.window);    /* credit window */
  122.                 prt->out.port = intswap(p->t.source);
  123.                 prt->in.nxt = longswap(p->t.seq) + 1;
  124. /*
  125. *  set the necessary fields in the outgoing TCP packet
  126. */
  127.                 prt->tcpout.t.dest = p->t.source;
  128.                 prt->tcpout.t.ack = longswap(prt->in.nxt);
  129.                 prt->tcpout.t.flags = TSYN | TACK;
  130.                 prt->tcpout.t.hlen = 24 << 2;
  131. /*
  132. *  note that the maxmimum segment size is installed by 'netlisten()'
  133. *  hence the header length is 24, not 20
  134. */
  135.                                 
  136. /*
  137. *  initialize all of the low-level transmission stuff (IP and lower)
  138. */
  139.                 movebytes(prt->tcps.dest,p->i.ipsource,4);
  140.                 movebytes(prt->tcpout.i.ipdest,p->i.ipsource,4);
  141.                 movebytes(prt->tcpout.d.dest,p->d.me,DADDLEN);
  142. #ifdef MAC
  143. /*
  144. *   look up address in the arp cache if using Atalk encapsulation
  145. */
  146.     if (!nnemac) {
  147.         unsigned char *pc;
  148.             pc = getdlayer(p->i.ipsource);
  149.             if (pc != NULL)
  150.                 movebytes(prt->tcpout.d.dest,pc,DADDLEN);
  151.             else
  152.                 return(0);        /* no hope this time */
  153.     }
  154. #endif
  155.  
  156.                 tcpsend(prt,4);
  157.                 prt->state = SSYNR;        /* syn received */
  158.             }
  159.             break;
  160.         case SSYNR:
  161.             if (!(p->t.flags & TACK)) {
  162.                 tcpsend(prt,4);
  163.                 break;                    /* not the right one */
  164.             }
  165.             prt->tcpout.t.hlen = 20 << 2;
  166.             prt->out.lasttime = time(NULL);            /* don't need response */
  167.             prt->out.nxt++;                            /* count SYN as sent */
  168.             prt->out.ack = longswap(p->t.ack);         /* starting ACK value */
  169.             prt->out.size = intswap(p->t.window);    /* allowed window */
  170.             prt->tcpout.t.flags = TACK;        /* starting ACK flag */
  171.             prt->state = SEST;                /* drop through to established */
  172.             netputevent(CONCLASS,CONOPEN,pnum);
  173.             checkmss(prt,p,hlen);            /* see if MSS option is there */
  174.  
  175.                                             /* fall through */
  176.         case SEST:
  177.             /* normal data transmission */
  178.             /*
  179.             *  check and accept a possible piggybacked ack
  180.             */
  181.             ackcheck(prt,p,pnum);
  182.  
  183.             estab1986(prt,p,tlen,hlen);
  184.             return(0);
  185.         case SSYNS:                /* check to see if it ACKS correctly */
  186.                                 /* remember that tcpout is pre-set-up */
  187.             if (p->t.flags & TACK) {        /* It is ACKING us */
  188.                 if (longswap(p->t.ack) != prt->out.nxt) {
  189.                     netposterr(401);
  190.                     return(1);
  191.                 }
  192.             }
  193.             if (p->t.flags & TRESET) {
  194.                 netposterr(507);
  195.                 prt->state = SCLOSED;
  196.                 netputuev(CONCLASS,CONCLOSE,pnum);
  197.                 return(1);
  198.             }
  199.             if (p->t.flags & TSYN) {            /* need to send ACK */
  200.                 prt->tcpout.t.flags = TACK;
  201.                 prt->in.nxt = longswap(p->t.seq) + 1;
  202.                 prt->tcpout.t.ack = longswap(prt->in.nxt);
  203.                 prt->out.ack = longswap(p->t.ack);
  204.                 prt->out.size = intswap(p->t.window);    /* credit window */
  205.                 prt->out.lasttime = 0L;
  206.                 if (p->t.flags & TACK) {
  207.                     prt->state = SEST;
  208.                     netputevent(CONCLASS,CONOPEN,pnum);
  209.                     checkmss(prt,p,hlen);
  210.                 }
  211.                 else
  212.                     prt->state = SSYNR;        /* syn received */
  213.             }
  214.             break;
  215.         case SCWAIT:
  216.             ackcheck(prt,p,pnum);
  217.             if (!prt->in.contain) {
  218.                 prt->tcpout.t.flags = TFIN | TACK;
  219.                 prt->out.lasttime = 0L;
  220.                 prt->state = SLAST;
  221.             }
  222.             break;
  223.         case SLAST:
  224.             /* check ack of FIN, or reset to see if we are done */
  225.             if ((p->t.flags & TRESET) || (longswap(p->t.ack) == prt->out.nxt+1))
  226.                 prt->state = SCLOSED;
  227.             break;
  228.         case SFW1:                            /* waiting for ACK of FIN */
  229.                                             /* throw away data */
  230.             prt->in.nxt = longswap(p->t.seq)+tlen-hlen;
  231.             if (p->t.flags & TRESET)
  232.                 prt->state = SCLOSED;
  233.             else if (longswap(p->t.ack) != prt->out.nxt+1) {
  234.                 if (p->t.flags & TFIN) {    /* got FIN, no ACK for mine */
  235.                     prt->in.nxt++;                /* account for FIN byte */
  236.                     prt->tcpout.t.ack = longswap(prt->in.nxt);
  237.                     prt->tcpout.t.flags = TACK;    /* final byte has no FIN flag */
  238.                     prt->out.lasttime = 0L;        /* cause last ACK to be sent */
  239.                     prt->state = SCLOSING;
  240.                 }
  241.                 else {
  242.                     prt->tcpout.t.ack = longswap(prt->in.nxt);
  243.                     prt->tcpout.t.flags = TACK | TFIN;
  244.                     prt->out.lasttime = 0L;
  245.                 }
  246.             }
  247.             else if (p->t.flags & TFIN) {    /* ACK and FIN */
  248.                 prt->in.nxt++;                /* account for his FIN flag */
  249.                 prt->out.nxt++;                /* account for my FIN */
  250.                 prt->tcpout.t.ack = longswap(prt->in.nxt);
  251.                 prt->tcpout.t.flags = TACK;    /* final byte has no FIN flag */
  252.                 prt->out.lasttime = 0L;        /* cause last ACK to be sent */
  253.                 prt->state = STWAIT;        /* we are done */
  254.             }
  255.             else {                            /* got ACK, no FIN */
  256.                 prt->out.nxt++;                /* account for my FIN byte */
  257.                 prt->tcpout.t.flags = TACK;    /* final pkt has no FIN flag */
  258.                 prt->state = SFW2;
  259.             }
  260.             break;
  261.         case SFW2:                                /* want FIN */
  262.             prt->in.nxt = longswap(p->t.seq)+tlen-hlen;
  263.             if (p->t.flags & TRESET)
  264.                 prt->state = SCLOSED;
  265.             else if (p->t.flags & TFIN) {        /* we got FIN */
  266.                 prt->in.nxt++;                    /* count his FIN byte */
  267.                 prt->tcpout.t.ack = longswap(prt->in.nxt);
  268.                 prt->out.lasttime = 0L;        /* cause last ACK to be sent */
  269.                 prt->state = STWAIT;
  270.             }
  271.             break;
  272.         case SCLOSING:                        /* want ACK of FIN */
  273.             if (p->t.flags & TRESET)
  274.                 prt->state = SCLOSED;
  275.             else if (!ackcheck(prt,p,pnum)) {
  276.                 prt->out.nxt++;                /* account for my FIN byte */
  277.                 prt->state = STWAIT;        /* time-wait state next */
  278.             }
  279.             break;
  280.         case STWAIT:                        /* ack FIN again? */
  281.             if (p->t.flags & TRESET)
  282.                 prt->state = SCLOSED;
  283.             if (p->t.flags & TFIN)             /* only if he wants it */
  284.                 prt->out.lasttime = 0L;
  285.             if (prt->out.lasttime && 
  286.                 (prt->out.lasttime + WAITTIME < time(NULL))) 
  287.                 prt->state = SCLOSED;
  288.             break;            
  289.         case SCLOSED:
  290.             prt->in.port = prt->out.port = 0;
  291.             break;
  292.         default:
  293.             netposterr(403);            /* unknown tcp state */
  294.             break;
  295.     }
  296.  
  297.     return(0);
  298.  
  299. }
  300.  
  301. /**********************************************************************/
  302. /*  checkmss
  303. *  Look at incoming SYN,ACK packet and check for the options field
  304. *  containing a TCP Maximum segment size option.  If it has one,
  305. *  then set the port's internal value to make sure that it never
  306. *  exceeds that segment size.
  307. */
  308. checkmss(prt,p,hlen)
  309.     int hlen;
  310.     struct port *prt;
  311.     TCPKT *p;
  312.     {
  313.     unsigned int i;
  314. /*
  315. *  check header for maximum segment size option
  316. */
  317.     if (hlen > 20 && p->x.options[0] == 2 && p->x.options[1] == 4) {
  318.         movebytes(&i,&p->x.options[2],2);    /* swapped value of maxseg */
  319.         i = intswap(i);
  320.         if (i < prt->sendsize)                /* we have our own limits too */
  321.             prt->sendsize = i;
  322.     }
  323. }
  324.  
  325. /**********************************************************************/
  326. /* tcpreset
  327. *  Send a reset packet back to sender
  328. *  Use the packet which just came in as a template to return to
  329. *  sender.  Fill in all of the fields necessary and dlayersend it back.
  330. */
  331. tcpreset(t)
  332.     TCPKT *t;
  333.     {
  334.     uint tport;
  335.     struct pseudotcp xxx;
  336.  
  337.     if (t->t.flags & TRESET)        /* don't reset a reset */
  338.         return(1);
  339.  
  340. /*
  341. *  swap TCP layer portions for sending back
  342. */
  343.     if (t->t.flags & TACK) {
  344.         t->t.seq = t->t.ack;        /* ack becomes next seq # */
  345.         t->t.ack = 0L;                /* ack # is 0 */
  346.     }
  347.     else {
  348.         t->t.ack = longswap(longswap(t->t.seq)+t->i.tlen-sizeof(IPLAYER));
  349.         t->t.seq = 0L;
  350.     }
  351.  
  352.     t->t.flags = TRESET;
  353.     tport = t->t.source;                    /* swap port #'s */
  354.     t->t.source = t->t.dest;
  355.     t->t.dest = tport;
  356.     t->t.hlen = 20 << 2;                    /* header len */
  357.     t->t.window = 0;
  358.  
  359. /*
  360. *  create pseudo header for checksum
  361. */
  362.     xxx.z = 0;
  363.     xxx.proto = t->i.protocol;
  364.     xxx.tcplen = intswap(20);
  365.     movebytes(xxx.source,t->i.ipsource,4);
  366.     movebytes(xxx.dest,t->i.ipdest,4);
  367.  
  368.     t->t.check = 0;    
  369.     t->t.check = tcpcheck(&xxx,&t->t,sizeof(struct tcph));
  370. /*
  371. *  IP and data link layers
  372. */    
  373.     movebytes(t->i.ipdest,t->i.ipsource,4);  /* machine it came from */
  374.     movebytes(t->i.ipsource,nnipnum,4); 
  375.     t->i.tlen = intswap(sizeof(IPLAYER)+sizeof(TCPLAYER));
  376.     t->i.ident = nnipident++;
  377.     t->i.ttl = 30;
  378.     t->i.check = 0;
  379.     t->i.check = ipcheck(&t->i,10);
  380.  
  381.     movebytes(t->d.dest,t->d.me,DADDLEN);    /* data link address */
  382.     movebytes(t->d.me,blankd.me,DADDLEN);    /* my address */
  383.     
  384.     return(dlayersend(t,sizeof(DLAYER)+sizeof(IPLAYER)+sizeof(TCPLAYER)));
  385.  
  386. }
  387.  
  388. /***************************************************************************/
  389. /*  tcpsend
  390. *     transmits a TCP packet.  
  391. *
  392. *   For IP:
  393. *      sets ident,check,totallen
  394. *   For TCP:
  395. *      sets seq and window from port information,
  396. *        fills in the pseudo header and computes the checksum.
  397. *      Assumes that all fields not filled in here are filled in by the
  398. *      calling proc or were filled in by makeport(). 
  399. *      (see all inits in protinit)
  400. *
  401. */
  402. tcpsend(pport,dlen)
  403.     int dlen;
  404.     struct port *pport;
  405.     {
  406.     struct port *p;
  407.  
  408.     p = pport;
  409.  
  410.     if (p == NULL) {
  411.         netposterr(404);
  412.         return(-1);
  413.     }
  414.  
  415. /*
  416. *  do IP header first
  417. */
  418.     p->tcpout.i.ident = intswap(nnipident++);
  419.     p->tcpout.i.tlen = intswap(sizeof(struct iph)+sizeof(struct tcph) + dlen);
  420.     p->tcpout.i.check = 0;                /* install checksum */
  421.     p->tcpout.i.check = ipcheck(&p->tcpout.i,10);
  422. /*
  423. *  do TCP header
  424. */
  425.     p->tcpout.t.seq = longswap(p->out.nxt);            /* bytes swapped */
  426.  
  427. /*
  428. *  if the port has some credit limit, use it instead of large
  429. *  window buffer.  Generally demanded by hardware limitations.
  430. */
  431.     if (p->credit < p->in.size)
  432.         p->tcpout.t.window = intswap(p->credit);
  433.     else
  434.         p->tcpout.t.window = intswap(p->in.size);    /* window size */
  435.  
  436. /*
  437. *  prepare pseudo-header for checksum
  438. */
  439.     p->tcps.tcplen = intswap(dlen+sizeof(TCPLAYER));
  440.     p->tcpout.t.check = 0;
  441.     p->tcpout.t.check = tcpcheck(&p->tcps,&p->tcpout.t,dlen+sizeof(struct tcph));
  442.  
  443.     p->out.lasttime = time(NULL);
  444.  
  445.     return(dlayersend(&p->tcpout,
  446.         sizeof(DLAYER)+sizeof(IPLAYER)+sizeof(TCPLAYER)+dlen));
  447.  
  448. }
  449.  
  450. /***************************************************************************/
  451. /*  ackcheck
  452. *   take an incoming packet and see if there is an ACK for the outgoing
  453. *   side.  Use that ACK to dequeue outgoing data.
  454. */
  455. ackcheck(p,t,pnum)
  456.     TCPKT *t;
  457.     struct port *p;
  458.     int pnum;
  459.     {
  460.     uint32 ak;
  461.     int32 rttl;
  462.     int i;
  463.  
  464.     if ((t->t.flags & TRESET) && (t->t.seq == p->tcpout.t.ack)) {
  465.         netposterr(405);
  466.         p->state = SCLOSED;
  467.         netputuev(CONCLASS,CONCLOSE,pnum);
  468.         return(1);
  469.     }
  470.  
  471.     if (!(t->t.flags & TACK))                /* check ACK flag */
  472.         return(1);                             /* if no ACK, no go */
  473.  
  474.     p->out.size = intswap(t->t.window);    /* allowable transmission size */
  475.  
  476. /*
  477. *  rmqueue any bytes which have been ACKed, update p->out.nxt to the
  478. *  new next seq number for outgoing.  Update send window.
  479. *
  480. */
  481.     ak = longswap(t->t.ack);            /* other side's ACK */
  482. /*
  483. *  Need to add code to check for wrap-around of sequence space
  484. *  for ak.  ak - p->out.ack may be affected by sequence wraparound.
  485. *  If you have good, efficient code for this, please send it to me.
  486. *
  487. *  If ak is not increasing (above p->out.nxt) then we should assume
  488. *  that it is a duplicate packet or one of those stupid keepalive
  489. *  packets that 4.2 sends out.
  490. */
  491.     if (ak > p->out.nxt) {
  492.         rmqueue(&p->out,(int)(ak - p->out.ack));    /* take off of queue */
  493.         p->out.nxt = ak;
  494.         p->out.ack = ak;
  495. /*
  496. *  Check to see if this acked our most recent transmission.  If so, adjust
  497. *  the RTO value to reflect the newly measured RTT.  This formula reduces
  498. *  the RTO value so that it gradually approaches the most recent round
  499. *  trip measurement.  When a packet is retransmitted, this value is
  500. *  doubled (exponential backoff).
  501. */
  502.         rttl = time(NULL) - p->out.lasttime;
  503.         if (!p->out.contain &&             /* just now emptied queue */
  504.             rttl < (long)(MAXRTO) && p->rto >= MINRTO) {
  505.             i = (int)(rttl);
  506.             i = ((p->rto-MINRTO)*3 + i + 1) >> 2;    /* smoothing function */
  507.             p->rto = i+MINRTO;
  508.         }
  509.  
  510.         if (p->out.size > 0)
  511.             p->out.lasttime = 0L;            /* forces xmit */
  512.         return(0);
  513.     }
  514.  
  515.     return(1);
  516.  
  517. }    
  518.  
  519. /***************************************************************************/
  520. /*  estab1986
  521. *   take a packet which has arrived for an established connection and
  522. *   put it where it belongs.
  523. */
  524. estab1986(prt,pkt,tlen,hlen)
  525.     struct port *prt;
  526.     TCPKT *pkt;
  527.     int tlen,hlen;
  528.     {
  529.     int dlen;
  530.     uint32 sq,want;
  531.  
  532.     dlen = tlen-hlen;
  533.     if (dlen <= 0) {                        /* only an ACK packet */
  534.         checkfin(prt,pkt);                    /* might still have FIN */
  535.         return(0);
  536.     }
  537.  
  538. /*
  539. *  see if we want this packet, or is it a duplicate?
  540. */
  541.     sq = longswap(pkt->t.seq);
  542.     want = prt->in.nxt;
  543.  
  544.     if (sq != want) {                /* we may want it, may not */
  545.  
  546.         if (sq < want && sq+dlen >= want) {      /* overlap */
  547.             hlen += want-sq;                    /* offset desired */
  548.             dlen -= want-sq;                    /* skip this much */
  549.         }
  550.         else {                                    /* tough it */
  551.             prt->out.lasttime = 0L;                /* make the ACK time out */
  552.             return(-1);
  553.         }
  554.     }
  555.  
  556. /*
  557. *  If we have room in the window, update the ACK field values
  558. */
  559.     if (prt->in.size >= dlen) {
  560.         prt->in.nxt += dlen;                /* new ack value */
  561.         prt->tcpout.t.ack = longswap(prt->in.nxt);
  562.         prt->in.size -= dlen;                /* new window size */
  563.  
  564.         prt->out.lasttime = 0L;                /* force timeout for ACK */
  565.  
  566.         enqueue(&prt->in,pkt->x.data+hlen-20,dlen);
  567.         netputuev(CONCLASS,CONDATA,pnum);    /* tell user about it */
  568.  
  569.         prt->in.lasttime = time(NULL);
  570.  
  571.     }
  572.  
  573.     else {                                    /* no room in input buffer */
  574.         prt->out.lasttime = 0L;                /* re-ack old sequence value */
  575.  
  576.     }
  577.  
  578. /* 
  579. *  Check the FIN bit to see if this connection is closing
  580. */
  581.     checkfin(prt,pkt);
  582.  
  583.     return(0);
  584. }
  585.  
  586. /***************************************************************************/
  587. /* checkfin
  588. *   Check the FIN bit of an incoming packet to see if the connection
  589. *   should be closing, ACK it if we need to.
  590. *   Half open connections immediately, automatically close.  We do
  591. *   not support them.  As soon as the incoming data is delivered, the
  592. *   connection will close.
  593. */
  594. checkfin(prt,pkt)
  595.     struct port *prt;
  596.     TCPKT *pkt;
  597.     {
  598.  
  599.     if (pkt->t.flags & TFIN) {        /* fin bit found */
  600.  
  601.         prt->in.nxt++;                /* count the FIN byte */
  602.         prt->state = SCWAIT;        /* close-wait */
  603.         prt->tcpout.t.ack = longswap(prt->in.nxt);    /* set ACK in packet */
  604.         prt->credit = 0;
  605.         prt->out.lasttime = 0L;        /* cause ACK to be sent */
  606.         netputuev(CONCLASS,CONCLOSE,pnum);
  607.  
  608. /*
  609. *   At this point, we know that we have received all data that the other
  610. *   side is allowed to send.  Some of that data may still be in the 
  611. *   incoming queue.  As soon as that queue empties, finish off the TCP
  612. *   close sequence.  We are not allowing the user to utilize a half-open
  613. *   connection, but we cannot close before the user has received all of
  614. *   the data from the incoming queue.
  615. */
  616.         if (!prt->in.contain) {                    /* data remaining? */
  617.             prt->tcpout.t.flags = TFIN | TACK;
  618.             tcpsend(prt,0);
  619.             prt->state = SLAST;
  620.         }
  621.     }
  622. }    
  623.  
  624.