📄 tcpout.c
字号:
//--------------------------------------------------------------------------
// Ip Stack
//--------------------------------------------------------------------------
// TCPOUT.C
//
// TCP Out Functions
//
// Author: Michael A. Denio
// Copyright 1999 by Texas Instruments Inc.
//-------------------------------------------------------------------------
#include <stkmain.h>
#include "tcp.h"
//#define TCP_DEBUG
//
// TCP Flags Based on State
//
static UINT8 tcp_outflags[] = {
TCP_RST | TCP_ACK, // CLOSED
0, // LISTEN
TCP_SYN, // SYNSENT
TCP_SYN | TCP_ACK, // SYNRCVD
TCP_ACK, // ESTAB
TCP_ACK, // CLOSEWAIT
TCP_FIN|TCP_ACK, // FINWAIT1
TCP_FIN|TCP_ACK, // CLOSING
TCP_FIN|TCP_ACK, // LASTACK
TCP_ACK, // FINWAIT2
TCP_ACK }; // TIMEWAIT
//--------------------------------------------------------------------
// TcpOutput()
//
// Called when a TCP may need to be sent
//--------------------------------------------------------------------
int TcpOutput( TCPPROT *pt )
{
int sendmore, error = 0;
INT32 mss,off,win,len,optlen,tmp,TxbTotal;
UINT32 dwTmp;
UINT8 flags;
UINT8 opt[TCP_MAX_OPTIONS];
HANDLE hPkt,hFrag;
uint Offset,IPHdrLen;
UINT8 *pb,*pPayload;
TCPHDR *pTcpHdr;
sendagain:
sendmore = 0;
// Get the total bytes left to send
// We'll use this value several times
TxbTotal = SBGetTotal( pt->hSBTx );
// Get the mss
// We compare the uint mss to UINT32 values quite a bit
mss = (INT32)pt->t_mss;
// Determine length of data to be transmitted
// If there are some critical flags set, send
// If we've been idle for longer than the current retransmit time,
// go back to "slow start" by resetting the congestion window
if( pt->snd_una==pt->snd_max && pt->t_tidle > pt->t_trtx )
pt->snd_cwnd = mss;
//
// off = Offset from beginning of send buffer to the first byte to send
// win = Min of adverstised or congestion window
//
off = pt->snd_nxt - pt->snd_una;
win = (pt->snd_wnd < pt->snd_cwnd) ? pt->snd_wnd : pt->snd_cwnd;
// Get default TCP flags based on the current state
flags = tcp_outflags[ pt->t_state ];
// Check for persist condition
if( pt->t_flags & TF_PERSIST )
{
// If the window is still closed, open it window 1 byte
// else reset the persist state.
if( !win )
{
win = 1;
// If there is more data to send, make sure FIN is not set
if( off < TxbTotal )
flags &= ~TCP_FIN;
}
else
{
// Clear Persist
pt->t_flags &= ~TF_PERSIST;
pt->dwTicksPersist = 0;
pt->t_rtxindex = 0;
// Set NEEDOUTPUT to use what window is available
// Otherwise we may decide not to send again!
pt->t_flags |= TF_NEEDOUTPUT;
}
}
//
// Set len = the max number of bytes we can send this time
// This is bound by the current send window, or the number of bytes
// we have left to send.
//
if( win < TxbTotal )
len = win - off;
else
len = TxbTotal - off;
// Check for a problem (len < 0) - we sent too much
if( len < 0 )
{
len = 0;
// We are either in a normal FIN wait state:
// (win > off > TxbTotal) ,
// or our send window shrank. If the window shrank to zero,
// back up to last ACK'd data and turn off Rexmt. This will
// cause us to drop into PERSIST if we don't send.
if( !win )
{
pt->snd_nxt = pt->snd_una;
pt->dwTicksRexmt = 0;
}
}
// See if we have to bust up our send into multiple segments this pass
if( len > mss )
{
len = mss;
sendmore = 1;
}
// Clear any pending FIN flag if this isn't the last data to go
if( SEQ_LT( pt->snd_nxt + len, pt->snd_una + TxbTotal ) )
flags &= ~TCP_FIN;
// Setup the window size we wish to advertise
// This is the space we have left in the RX buffer
// NOTE: "win" is now the ADVERTISED WINDOW SIZE
win = SBGetSpace( pt->hSBRx );
if( win < 0 )
win = 0;
if( win > TCP_MAXWIN )
win = TCP_MAXWIN;
//
// Check for various send conditions
//
if( len )
{
// Send if we have a full segment
if( len == mss )
goto send;
// If we're emptying out the send buffer and NOPUSH is not set, send
if( !(pt->t_flags & TF_NOPUSH) && len+off >= TxbTotal )
goto send;
// If forced output (from persist or ENOBUFS), goto send
if( pt->t_flags & (TF_PERSIST|TF_NEEDOUTPUT) )
goto send;
// If receiver's window is at least half open, send
if( len >= (INT32)(pt->max_sndwnd/2) )
goto send;
// Send on a retransmit (here snd_nxt was set back to snd_una)
if( SEQ_LT( pt->snd_nxt, pt->snd_max ) )
goto send;
}
// Send if we must ACK
if( pt->t_flags & TF_ACKNOW )
goto send;
// Send if we are sending the SYN or RST flag
if( flags & (TCP_SYN | TCP_RST) )
goto send;
// Send if we have urgent data
if( SEQ_GT( pt->snd_up, pt->snd_una ) )
goto send;
//
// Check for Window Update Conditions
//
if( win > 0 )
{
// Get the number of bytes that our receive window has opened
tmp = win - (pt->rcv_adv - pt->rcv_nxt);
// If the window had opened by 2 segments, advertise new window
if( tmp >= 2 * mss )
goto send;
// If the new window is at least half our receive buffer, advertise
if( tmp >= SBGetMax(pt->hSBRx)/2 )
goto send;
}
// Lastly, we'll send if FIN is set and we haven't sent it yet, or
// if we're here because of a re-transmit
if( (flags & TCP_FIN) &&
( !(pt->t_flags & TF_SENTFIN) || (pt->snd_nxt == pt->snd_una) ) )
goto send;
// If we get here, we were unable to send, or decided not to
// send. We also know that we are not in the PERSIST state.
// If there is data to send and there is no retransmit in progress,
// then we'll go into PERSIST mode now to wait for a larger window.
if( TxbTotal && !pt->dwTicksRexmt )
{
pt->t_rtxindex = 0; // Reset backoff for initial persist
TcpSetPersist( pt ); // Initialize Persist
}
return(0);
send:
//
// If we get here, we're going to send something
//
optlen = 0;
// If SYN is being sent we'll also include MAXSEG
if( flags & TCP_SYN )
{
// Reset sending sequence number to our assigned ISS
pt->snd_nxt = pt->iss;
// Validate the route and the route metrics for this socket.
// This will return the MSS we advertise, but not the one
// we actually restrict ourselves to.
tmp = TcpValidateMetrics( pt, 0 );
// Add in options if not disabled
if( !(pt->t_flags & TF_NOOPT) )
{
// Send our max seg size
opt[0] = TCPOPT_MAXSEG; // TCPOPT_MAXSEG
opt[1] = TCPOLEN_MAXSEG; // MAXSEG Size
opt[2] = (UINT8)(tmp >> 8);
opt[3] = (UINT8)(tmp);
optlen += 4;
}
}
// Adjust send length to make room for options
if( len > mss - optlen )
{
len = mss - optlen;
sendmore = 1;
}
//
// Create the packet
// Payload = len
// Reserve = TCPHDR_SIZE + optlen
//
if( !(hPkt = SockCreatePacket( pt->hSock, (uint)len,
TCPHDR_SIZE+(uint)optlen )) )
{
//
// Out of buffers condition
//
pt->t_flags |= TF_NEEDOUTPUT;
tcps.dwSndNoBufs++;
if( pt->t_state != TSTATE_ESTAB )
return( ENOBUFS );
else
return( 0 );
}
// Get the frag
hFrag = PktGetFrag( hPkt );
// Get the buffer parameters
pb = FragGetBufParams( hFrag, 0, 0, &Offset );
// Get the IP header len
IPHdrLen = PktGetSizeNet( hPkt );
// Assign a UDP header pointer
pTcpHdr = (TCPHDR *)(pb + Offset + IPHdrLen);
// Get pointer to payload
pPayload = pb + Offset + IPHdrLen + TCPHDR_SIZE + optlen;
//
// Build the TCP Packet
//
if( len )
{
//
// We're Sending Data
//
// Update the send type stats
if( pt->t_flags & TF_PERSIST )
tcps.dwSndProbe++; // Window probes sent
else if( SEQ_LT(pt->snd_nxt,pt->snd_max) )
{
tcps.dwSndRexmitPack++; // Retransmit packets
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -