home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 1
/
GoldFishApril1994_CD1.img
/
d2xx
/
d225
/
amigatcp
/
src
/
tcpout.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-06-24
|
6KB
|
205 lines
#include "machdep.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "tcp.h"
int16 tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */
/* Send a segment on the specified connection. One gets sent only
* if there is data to be sent or if "force" is non zero
*/
void
tcp_output(tcb)
register struct tcb *tcb;
{
struct pseudo_header ph;
struct mbuf *hbp;
int16 hsize; /* Size of header */
struct tcp_header *tcph;
struct mss *mssp;
int16 ssize; /* Size of current segment being sent,
* including SYN and FIN flags */
int16 dsize; /* Size of segment less SYN and FIN */
int16 usable; /* Usable window */
int16 sent; /* Sequence count (incl SYN/FIN) already in the pipe */
if(tcb == NULLTCB)
return;
switch(tcb->state){
case LISTEN:
case CLOSED:
return; /* Don't send anything */
}
for(;;){
sent = tcb->snd.ptr - tcb->snd.una;
/* If this is a retransmission, send only the oldest segment
* (first-only retransmission policy)
*/
if(tcb->retry != 0 && sent != 0)
break;
/* There can only be one outstanding segment in this state
* since the other end would reject any segment without SYN
*/
if(tcb->state == SYN_SENT && sent != 0)
break;
if(tcb->snd.wnd == 0){
/* Allow only one closed-window probe at a time */
if(sent != 0)
break;
/* Force a closed-window probe */
usable = 1;
} else {
/* usable window = offered window - unacked bytes in transit */
usable = tcb->snd.wnd - sent;
/* John Nagle's "single outstanding segment" rule.
* Allow only one segment in the pipeline unless there is enough
* unsent data to form at least one maximum-sized segment.
*/
if(sent != 0 && tcb->sndcnt - sent < tcb->mss){
usable = 0;
}
/* Silly window avoidance. Don't send anything if the usable window
* is less than a quarter of the offered window.
* This test comes into play only when the offered window is at
* least 4 times the MSS; otherwise Nagle's test is sufficient
* to prevent SWS.
*/
else if(usable < tcb->snd.wnd/4){
usable = 0;
}
}
/* Compute size of segment to send. This is either the usable
* window, the mss, or the amount we have on hand, whichever is less.
* (I don't like optimistic windows)
*/
ssize = min(tcb->sndcnt - sent,usable);
ssize = min(ssize,tcb->mss);
dsize = ssize;
if(ssize == 0 && tcb->force == 0)
break; /* No need to send anything */
/* Determine size of TCP header and allocate mbuf.
* If sending SYN, allow space for the MSS option
*/
switch(tcb->state){
case SYN_SENT:
case SYN_RECEIVED:
hsize = sizeof(struct tcp_header) + sizeof(struct mss);
break;
default:
hsize = sizeof(struct tcp_header);
break;
}
if((hbp = alloc_mbuf(hsize)) == NULLBUF)
break; /* No room to form a packet */
tcb->force = 0; /* Only one forced segment! */
hbp->cnt = hsize;
tcph = (struct tcp_header *)hbp->data;
tcph->source = htons(tcb->conn.local.port);
tcph->dest = htons(tcb->conn.remote.port);
tcph->offset = hsize/sizeof(int32) << DSHIFT;
tcph->flags = 0;
/* Set the SYN and ACK flags according to the state we're in. It is
* assumed that if this segment is associated with a state transition,
* then the state change will already have been made. This allows
* this routine to be called from a retransmission timeout with
* force=1.
* If SYN is being sent, adjust the dsize counter so we'll
* try to get the right amount of data off the send queue.
*/
switch(tcb->state){
case SYN_SENT:
case SYN_RECEIVED:
if(tcb->snd.ptr == tcb->iss){
tcph->flags = SYN;
dsize--;
}
/* Also send MSS */
mssp = (struct mss *)(tcph + 1);
mssp->kind = MSS_KIND;
mssp->length = MSS_LENGTH;
mssp->mss = htons(tcp_mss);
}
switch(tcb->state){
case SYN_RECEIVED:
case ESTABLISHED:
case CLOSE_WAIT:
case FINWAIT2:
case TIME_WAIT:
case CLOSING:
case FINWAIT1:
case LAST_ACK:
tcph->flags |= ACK;
break;
}
tcph->seq = htonl(tcb->snd.ptr);
tcph->ack = htonl(tcb->rcv.nxt);
tcph->wnd = htons(tcb->rcv.wnd);
tcph->checksum = 0;
tcph->up = 0;
/* Now try to extract some data from the send queue.
* Since SYN and FIN occupy sequence space and are reflected
* in sndcnt but don't actually sit in the send queue,
* dup_p will return one less than dsize if a FIN needs to be sent.
*/
if(dsize != 0){
if(dup_p(&hbp->next,tcb->sndq,sent,dsize) != dsize){
/* We ran past the end of the send queue; send a FIN */
tcph->flags |= FIN;
dsize--;
}
}
/* If the entire send queue will now be in the pipe, set the
* push flag
*/
if(dsize != 0 && sent + ssize == tcb->sndcnt)
tcph->flags |= PSH;
tcb->snd.ptr += ssize;
/* If this is the first transmission of a range of sequence
* numbers, record it so we'll accept acknowledgments
* for it later
*/
if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
tcb->snd.nxt = tcb->snd.ptr;
/* Fill in fields of pseudo IP header */
ph.source = tcb->conn.local.address;
ph.dest = tcb->conn.remote.address;
ph.protocol = TCP_PTCL;
ph.zero = 0;
ph.length = hsize + dsize;
/* Compute checksum over pseudo-header, TCP header and data,
* and pass it off to IP
*/
tcph->checksum = cksum(&ph,hbp,ph.length);
/* If we're sending some data or flags, start retransmission
* timer if it isn't already running.
*/
if(ssize != 0){
if(tcb->timer.state != TIMER_RUN){
/* Never initialize the timer with zero; it won't run! */
tcb->timer.start = max(tcb->timer.start,1);
start_timer(&tcb->timer);
}
/* If round trip timer isn't running, start it */
if(seq_ge(tcb->snd.una,tcb->rttseq))
tcb->rttseq = tcb->snd.ptr;
}
ip_send(tcb->conn.local.address,tcb->conn.remote.address,
TCP_PTCL,tcb->tos,0,hbp,ph.length,0,0);
}
}