mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-21 21:01:15 -05:00
2798 lines
63 KiB
C++
2798 lines
63 KiB
C++
/*
|
|
* "$Id: tcpstream.cc,v 1.11 2007-03-01 01:09:39 rmf24 Exp $"
|
|
*
|
|
* TCP-on-UDP (tou) network interface for RetroShare.
|
|
*
|
|
* Copyright 2004-2006 by Robert Fernie.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License Version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
* USA.
|
|
*
|
|
* Please report all bugs and problems to "retroshare@lunamutt.com".
|
|
*
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "tcpstream.h"
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <math.h>
|
|
#include <limits.h>
|
|
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
|
|
/* Debugging for STATE change, and Startup SYNs */
|
|
#include "util/rsdebug.h"
|
|
#include "util/rsstring.h"
|
|
#include "util/rsrandom.h"
|
|
|
|
static struct RsLog::logInfo rstcpstreamzoneInfo = {RsLog::Default, "rstcpstream"};
|
|
#define rstcpstreamzone &rstcpstreamzoneInfo
|
|
|
|
/*
|
|
* #define DEBUG_TCP_STREAM 1
|
|
* #define DEBUG_TCP_STREAM_RETRANS 1
|
|
* #define DEBUG_TCP_STREAM_CLOSE 1
|
|
*/
|
|
|
|
//#define DEBUG_TCP_STREAM_RETRANS 1
|
|
//#define DEBUG_TCP_STREAM_CLOSE 1
|
|
|
|
/*
|
|
*#define DEBUG_TCP_STREAM_EXTRA 1
|
|
*/
|
|
|
|
/*
|
|
* #define TCP_NO_PARTIAL_READ 1
|
|
*/
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
int checkData(uint8 *data, int size, int idx);
|
|
int setupBinaryCheck(std::string fname);
|
|
#endif
|
|
|
|
static const uint32 kMaxQueueSize = 300; // Was 100, which means max packet size of 100k (smaller than max packet size).
|
|
static const uint32 kMaxPktRetransmit = 10;
|
|
static const uint32 kMaxSynPktRetransmit = 100; // 100 => 200secs = over 3 minutes startup
|
|
static const int TCP_STD_TTL = 64;
|
|
static const int TCP_DEFAULT_FIREWALL_TTL = 4;
|
|
|
|
static const double RTT_ALPHA = 0.875;
|
|
|
|
int dumpPacket(std::ostream &out, unsigned char *pkt, uint32_t size);
|
|
|
|
// platform independent fractional timestamp.
|
|
static double getCurrentTS();
|
|
|
|
TcpStream::TcpStream(UdpSubReceiver *lyr)
|
|
: tcpMtx("TcpStream"), inSize(0), outSizeRead(0), outSizeNet(0),
|
|
state(TCP_CLOSED),
|
|
inStreamActive(false),
|
|
outStreamActive(false),
|
|
outSeqno(0), outAcked(0), outWinSize(0),
|
|
inAckno(0), inWinSize(0),
|
|
maxWinSize(TCP_MAX_WIN),
|
|
keepAliveTimeout(TCP_ALIVE_TIMEOUT),
|
|
retransTimerOn(false),
|
|
retransTimeout(TCP_RETRANS_TIMEOUT),
|
|
retransTimerTs(0),
|
|
keepAliveTimer(0),
|
|
lastIncomingPkt(0),
|
|
lastSentAck(0),
|
|
lastSentWinSize(0),
|
|
initOurSeqno(0),
|
|
initPeerSeqno(0),
|
|
lastWriteTF(0),lastReadTF(0),
|
|
wcount(0), rcount(0),
|
|
errorState(0),
|
|
/* retranmission variables - init to large */
|
|
rtt_est(TCP_RETRANS_TIMEOUT),
|
|
rtt_dev(0),
|
|
congestThreshold(TCP_MAX_WIN),
|
|
congestWinSize(MAX_SEG),
|
|
congestUpdate(0),
|
|
ttl(0),
|
|
mTTL_period(0),
|
|
mTTL_start(0),
|
|
mTTL_end(0),
|
|
peerKnown(false),
|
|
udp(lyr)
|
|
{
|
|
sockaddr_clear(&peeraddr);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Stream Control! */
|
|
int TcpStream::connect(const struct sockaddr_in &raddr, uint32_t conn_period)
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
setRemoteAddress(raddr);
|
|
|
|
/* check state */
|
|
if (state != TCP_CLOSED)
|
|
{
|
|
if (state == TCP_ESTABLISHED)
|
|
{
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return 0;
|
|
}
|
|
else if (state < TCP_ESTABLISHED)
|
|
{
|
|
errorState = EAGAIN;
|
|
}
|
|
else
|
|
{
|
|
// major issues!
|
|
errorState = EFAULT;
|
|
}
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return -1;
|
|
}
|
|
|
|
/* setup Seqnos */
|
|
outSeqno = genSequenceNo();
|
|
initOurSeqno = outSeqno;
|
|
|
|
outAcked = outSeqno; /* min - 1 expected */
|
|
inWinSize = maxWinSize;
|
|
|
|
congestThreshold = TCP_MAX_WIN;
|
|
congestWinSize = MAX_SEG;
|
|
congestUpdate = outAcked + congestWinSize;
|
|
|
|
/* Init Connection */
|
|
/* send syn packet */
|
|
TcpPacket *pkt = new TcpPacket();
|
|
pkt -> setSyn();
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::connect() Send Init Pkt" << std::endl;
|
|
#endif
|
|
|
|
|
|
/* ********* SLOW START *************
|
|
* As this is the only place where a syn
|
|
* is sent ..... we switch the ttl to 0,
|
|
* and increment it as we retransmit the packet....
|
|
* This should help the firewalls along.
|
|
*/
|
|
|
|
setTTL(1);
|
|
|
|
mTTL_start = getCurrentTS();
|
|
mTTL_period = conn_period;
|
|
mTTL_end = mTTL_start + mTTL_period;
|
|
|
|
toSend(pkt);
|
|
/* change state */
|
|
state = TCP_SYN_SENT;
|
|
errorState = EAGAIN;
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream STATE -> TCP_SYN_SENT" << std::endl;
|
|
#endif
|
|
{
|
|
rslog(RSL_WARNING,rstcpstreamzone,"TcpStream::state => TCP_SYN_SENT (Connect)");
|
|
}
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int TcpStream::listenfor(const struct sockaddr_in &raddr)
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
setRemoteAddress(raddr);
|
|
|
|
/* check state */
|
|
if (state != TCP_CLOSED)
|
|
{
|
|
if (state == TCP_ESTABLISHED)
|
|
{
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return 0;
|
|
}
|
|
else if (state < TCP_ESTABLISHED)
|
|
{
|
|
errorState = EAGAIN;
|
|
}
|
|
else
|
|
{
|
|
// major issues!
|
|
errorState = EFAULT;
|
|
}
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return -1;
|
|
}
|
|
|
|
errorState = EAGAIN;
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* Stream Control! */
|
|
int TcpStream::close()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
cleanup();
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return 0;
|
|
}
|
|
|
|
int TcpStream::closeWrite()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
/* check state */
|
|
/* will always close socket.... */
|
|
/* if in TCP_ESTABLISHED....
|
|
* -> to state: TCP_FIN_WAIT_1
|
|
* and shutdown outward stream.
|
|
*/
|
|
|
|
/* if in CLOSE_WAIT....
|
|
* -> to state: TCP_LAST_ACK
|
|
* and shutdown outward stream.
|
|
* do this one first!.
|
|
*/
|
|
|
|
outStreamActive = false;
|
|
|
|
if (state == TCP_CLOSE_WAIT)
|
|
{
|
|
/* don't think we need to be
|
|
* graceful at this point...
|
|
* connection already closed by other end.
|
|
* XXX might fix later with scheme
|
|
*
|
|
* flag stream closed, and when outqueue
|
|
* emptied then fin will be sent.
|
|
*/
|
|
|
|
/* do nothing */
|
|
}
|
|
|
|
if (state == TCP_ESTABLISHED)
|
|
{
|
|
/* fire off the damned thing. */
|
|
/* by changing state */
|
|
|
|
/* again this is handled by internals
|
|
* the flag however indicates that
|
|
* no more data can be send,
|
|
* and once the queue empties
|
|
* the FIN will be sent.
|
|
*/
|
|
|
|
}
|
|
if (state == TCP_CLOSED)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::close() Flag Set" << std::endl;
|
|
#endif
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::close() pending" << std::endl;
|
|
#endif
|
|
errorState = EAGAIN;
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return -1;
|
|
}
|
|
|
|
bool TcpStream::isConnected()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
bool isConn = (state == TCP_ESTABLISHED);
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
|
|
return isConn;
|
|
}
|
|
|
|
int TcpStream::status(std::ostream &out)
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
int s = status_locked(out);
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
int TcpStream::status_locked(std::ostream &out)
|
|
{
|
|
|
|
int tmpstate = state;
|
|
|
|
// can leave the timestamp here as time()... rough but okay.
|
|
out << "TcpStream::status @ (" << time(NULL) << ")" << std::endl;
|
|
out << "TcpStream::state = " << (int) state << std::endl;
|
|
out << std::endl;
|
|
out << "writeBuffer: " << inSize << " + MAX_SEG * " << inQueue.size();
|
|
out << " bytes Queued for transmission" << std::endl;
|
|
out << "readBuffer: " << outSizeRead << " + MAX_SEG * ";
|
|
out << outQueue.size() << " + " << outSizeNet;
|
|
out << " incoming bytes waiting" << std::endl;
|
|
out << std::endl;
|
|
out << "inPkts: " << inPkt.size() << " packets waiting for processing";
|
|
out << std::endl;
|
|
out << "outPkts: " << outPkt.size() << " packets waiting for acks";
|
|
out << std::endl;
|
|
out << "us -> peer: nextSeqno: " << outSeqno << " lastAcked: " << outAcked;
|
|
out << " winsize: " << outWinSize;
|
|
out << std::endl;
|
|
out << "peer -> us: Expected SeqNo: " << inAckno;
|
|
out << " winsize: " << inWinSize;
|
|
out << std::endl;
|
|
out << std::endl;
|
|
|
|
return tmpstate;
|
|
}
|
|
|
|
int TcpStream::write_allowed()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
int ret = 1;
|
|
if (state == TCP_CLOSED)
|
|
{
|
|
errorState = EBADF;
|
|
ret = -1;
|
|
}
|
|
else if (state < TCP_ESTABLISHED)
|
|
{
|
|
errorState = EAGAIN;
|
|
ret = -1;
|
|
}
|
|
else if (!outStreamActive)
|
|
{
|
|
errorState = EBADF;
|
|
ret = -1;
|
|
}
|
|
|
|
if (ret < 1)
|
|
{
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return ret;
|
|
}
|
|
|
|
int maxwrite = (kMaxQueueSize - inQueue.size()) * MAX_SEG;
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return maxwrite;
|
|
}
|
|
|
|
int TcpStream::read_pending()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
/* error should be detected next time */
|
|
int maxread = int_read_pending();
|
|
if (state == TCP_CLOSED)
|
|
{
|
|
errorState = EBADF;
|
|
maxread = -1;
|
|
}
|
|
else if (state < TCP_ESTABLISHED)
|
|
{
|
|
errorState = EAGAIN;
|
|
maxread = -1;
|
|
}
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
|
|
return maxread;
|
|
}
|
|
|
|
/* INTERNAL */
|
|
int TcpStream::int_read_pending()
|
|
{
|
|
return outSizeRead + outQueue.size() * MAX_SEG + outSizeNet;
|
|
}
|
|
|
|
|
|
/* stream Interface */
|
|
int TcpStream::write(char *dta, int size) /* write -> pkt -> net */
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
int ret = 1; /* initial error checking */
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
static uint32 TMPtotalwrite = 0;
|
|
#endif
|
|
|
|
if (state == TCP_CLOSED)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::write() Error TCP_CLOSED" << std::endl;
|
|
#endif
|
|
errorState = EBADF;
|
|
ret = -1;
|
|
}
|
|
else if (state < TCP_ESTABLISHED)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::write() Error TCP Not Established" << std::endl;
|
|
#endif
|
|
errorState = EAGAIN;
|
|
ret = -1;
|
|
}
|
|
else if (inQueue.size() > kMaxQueueSize)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::write() Error EAGAIN" << std::endl;
|
|
#endif
|
|
errorState = EAGAIN;
|
|
ret = -1;
|
|
}
|
|
else if (!outStreamActive)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::write() Error TCP_CLOSED" << std::endl;
|
|
#endif
|
|
errorState = EBADF;
|
|
ret = -1;
|
|
}
|
|
|
|
if (ret < 1) /* check for initial error */
|
|
{
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::write() = Will Succeed " << size << std::endl;
|
|
std::cerr << "TcpStream::write() Write Start: " << TMPtotalwrite << std::endl;
|
|
std::cerr << printPktOffset(TMPtotalwrite, dta, size) << std::endl;
|
|
TMPtotalwrite += size;
|
|
#endif
|
|
|
|
|
|
if (size + inSize < MAX_SEG)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::write() Add Itty Bit" << std::endl;
|
|
std::cerr << "TcpStream::write() inData: " << (void *) inData;
|
|
std::cerr << " inSize: " << inSize << " dta: " << (void *) dta;
|
|
std::cerr << " size: " << size << " dest: " << (void *) &(inData[inSize]);
|
|
std::cerr << std::endl;
|
|
#endif
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::write() = " << size << std::endl;
|
|
#endif
|
|
memcpy((void *) &(inData[inSize]), dta, size);
|
|
inSize += size;
|
|
//std::cerr << "Small Packet - write to net:" << std::endl;
|
|
//std::cerr << printPkt(dta, size) << std::endl;
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return size;
|
|
}
|
|
|
|
/* otherwise must construct a dataBuffer.
|
|
*/
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::write() filling 1 dataBuffer" << std::endl;
|
|
std::cerr << "TcpStream::write() from inData(" << inSize << ")" << std::endl;
|
|
std::cerr << "TcpStream::write() + dta(" << MAX_SEG - inSize;
|
|
std::cerr << "/" << size << ")" << std::endl;
|
|
#endif
|
|
|
|
/* first create 1. */
|
|
dataBuffer *db = new dataBuffer;
|
|
memcpy((void *) db->data, (void *) inData, inSize);
|
|
|
|
|
|
int remSize = size;
|
|
memcpy((void *) &(db->data[inSize]), dta, MAX_SEG - inSize);
|
|
|
|
inQueue.push_back(db);
|
|
remSize -= (MAX_SEG - inSize);
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::write() remaining " << remSize << " bytes to load" << std::endl;
|
|
#endif
|
|
|
|
while(remSize >= MAX_SEG)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::write() filling whole dataBuffer" << std::endl;
|
|
std::cerr << "TcpStream::write() from dta[" << size-remSize << "]" << std::endl;
|
|
#endif
|
|
db = new dataBuffer;
|
|
memcpy((void *) db->data, (void *) &(dta[size-remSize]), MAX_SEG);
|
|
|
|
inQueue.push_back(db);
|
|
remSize -= MAX_SEG;
|
|
}
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::write() = " << size << std::endl;
|
|
#endif
|
|
|
|
if (remSize > 0)
|
|
{
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::write() putting last bit in inData" << std::endl;
|
|
std::cerr << "TcpStream::write() from dta[" << size-remSize << "] size: ";
|
|
std::cerr << remSize << std::endl;
|
|
#endif
|
|
memcpy((void *) inData, (void *) &(dta[size-remSize]), remSize);
|
|
inSize = remSize;
|
|
}
|
|
else
|
|
{
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::write() Data fitted exactly in dataBuffer!" << std::endl;
|
|
#endif
|
|
inSize = 0;
|
|
}
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return size;
|
|
}
|
|
|
|
int TcpStream::read(char *dta, int size) /* net -> pkt -> read */
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
static uint32 TMPtotalread = 0;
|
|
#endif
|
|
/* max available data is
|
|
* outDataRead + outQueue + outDataNet
|
|
*/
|
|
|
|
int maxread = outSizeRead + outQueue.size() * MAX_SEG + outSizeNet;
|
|
int ret = 1; /* used only for initial errors */
|
|
|
|
if (state == TCP_CLOSED)
|
|
{
|
|
errorState = EBADF;
|
|
ret = -1;
|
|
}
|
|
else if (state < TCP_ESTABLISHED)
|
|
{
|
|
errorState = EAGAIN;
|
|
ret = -1;
|
|
}
|
|
else if ((!inStreamActive) && (maxread == 0))
|
|
{
|
|
// finished stream.
|
|
ret = 0;
|
|
}
|
|
else if (maxread == 0)
|
|
{
|
|
/* must wait for more data */
|
|
errorState = EAGAIN;
|
|
ret = -1;
|
|
}
|
|
|
|
if (ret < 1) /* if ret has been changed */
|
|
{
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return ret;
|
|
}
|
|
|
|
if (maxread < size)
|
|
{
|
|
#ifdef TCP_NO_PARTIAL_READ
|
|
if (inStreamActive)
|
|
{
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::read() No Partial Read! ";
|
|
std::cerr << "Can only supply " << maxread << " of ";
|
|
std::cerr << size;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
errorState = EAGAIN;
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return -1;
|
|
}
|
|
#endif /* TCP_NO_PARTIAL_READ */
|
|
size = maxread;
|
|
}
|
|
|
|
/* if less than outDataRead size */
|
|
if (((unsigned) (size) < outSizeRead) && (outSizeRead))
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() Add Itty Bit" << std::endl;
|
|
std::cerr << "TcpStream::read() outSizeRead: " << outSizeRead;
|
|
std::cerr << " size: " << size << " remaining: " << outSizeRead - size;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
memcpy(dta,(void *) outDataRead, size);
|
|
memmove((void *) outDataRead,
|
|
(void *) &(outDataRead[size]), outSizeRead - (size));
|
|
outSizeRead -= size;
|
|
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() = Succeeded " << size << std::endl;
|
|
std::cerr << "TcpStream::read() Read Start: " << TMPtotalread << std::endl;
|
|
std::cerr << printPktOffset(TMPtotalread, dta, size) << std::endl;
|
|
#endif
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
checkData((uint8 *) dta, size, TMPtotalread);
|
|
TMPtotalread += size;
|
|
#endif
|
|
|
|
/* can allow more in! - update inWinSize */
|
|
UpdateInWinSize();
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return size;
|
|
}
|
|
|
|
/* move the whole of outDataRead. */
|
|
if (outSizeRead)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() Move All outSizeRead" << std::endl;
|
|
std::cerr << "TcpStream::read() outSizeRead: " << outSizeRead;
|
|
std::cerr << " size: " << size;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
memcpy(dta,(void *) outDataRead, outSizeRead);
|
|
|
|
}
|
|
|
|
int remSize = size - outSizeRead;
|
|
outSizeRead = 0;
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() remaining size: " << remSize << std::endl;
|
|
#endif
|
|
|
|
while((outQueue.size() > 0) && (remSize > 0))
|
|
{
|
|
dataBuffer *db = outQueue.front();
|
|
outQueue.pop_front(); /* remove */
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() Taking Data from outQueue" << std::endl;
|
|
#endif
|
|
|
|
/* load into outDataRead */
|
|
if (remSize < MAX_SEG)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() Partially using Segment" << std::endl;
|
|
std::cerr << "TcpStream::read() moving: " << remSize << " to dta @: " << size-remSize;
|
|
std::cerr << std::endl;
|
|
std::cerr << "TcpStream::read() rest to outDataRead: " << MAX_SEG - remSize;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
memcpy((void *) &(dta[(size)-remSize]), (void *) db->data, remSize);
|
|
memcpy((void *) outDataRead, (void *) &(db->data[remSize]), MAX_SEG - remSize);
|
|
outSizeRead = MAX_SEG - remSize;
|
|
|
|
delete db;
|
|
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() = Succeeded " << size << std::endl;
|
|
std::cerr << "TcpStream::read() Read Start: " << TMPtotalread << std::endl;
|
|
std::cerr << printPktOffset(TMPtotalread, dta, size) << std::endl;
|
|
#endif
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
checkData((uint8 *) dta, size, TMPtotalread);
|
|
TMPtotalread += size;
|
|
#endif
|
|
|
|
/* can allow more in! - update inWinSize */
|
|
UpdateInWinSize();
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return size;
|
|
}
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() Move Whole Segment to dta @ " << size-remSize << std::endl;
|
|
#endif
|
|
|
|
/* else copy whole segment */
|
|
memcpy((void *) &(dta[(size)-remSize]), (void *) db->data, MAX_SEG);
|
|
remSize -= MAX_SEG;
|
|
delete db;
|
|
}
|
|
|
|
/* assumes that outSizeNet >= remSize due to initial
|
|
* constraint
|
|
*/
|
|
if ((remSize > 0))
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() Using up : " << remSize;
|
|
std::cerr << " last Bytes, leaving: " << outSizeNet - remSize << std::endl;
|
|
#endif
|
|
|
|
|
|
memcpy((void *) &(dta[(size)-remSize]),(void *) outDataNet, remSize);
|
|
outSizeNet -= remSize;
|
|
if (outSizeNet > 0)
|
|
{
|
|
/* move to the outDataRead */
|
|
memcpy((void *) outDataRead,(void *) &(outDataNet[remSize]), outSizeNet);
|
|
outSizeRead = outSizeNet;
|
|
outSizeNet = 0;
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() moving last of outSizeNet to outSizeRead: " << outSizeRead;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() = Succeeded " << size << std::endl;
|
|
std::cerr << "TcpStream::read() Read Start: " << TMPtotalread << std::endl;
|
|
std::cerr << printPktOffset(TMPtotalread, dta, size) << std::endl;
|
|
#endif
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
checkData((uint8 *) dta, size, TMPtotalread);
|
|
TMPtotalread += size;
|
|
#endif
|
|
|
|
/* can allow more in! - update inWinSize */
|
|
UpdateInWinSize();
|
|
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return size;
|
|
}
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
std::cerr << "TcpStream::read() = Succeeded " << size << std::endl;
|
|
std::cerr << "TcpStream::read() Read Start: " << TMPtotalread << std::endl;
|
|
std::cerr << printPktOffset(TMPtotalread, dta, size) << std::endl;
|
|
#endif
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
checkData((uint8 *) dta, size, TMPtotalread);
|
|
TMPtotalread += size;
|
|
#endif
|
|
|
|
/* can allow more in! - update inWinSize */
|
|
UpdateInWinSize();
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return size;
|
|
}
|
|
|
|
|
|
/* Callback from lower Layers */
|
|
void TcpStream::recvPkt(void *data, int size)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::recvPkt()";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
uint8 *input = (uint8 *) data;
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::recvPkt() Past Lock!";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
if (state > TCP_SYN_RCVD)
|
|
{
|
|
int availRead = outSizeRead + outQueue.size() * MAX_SEG + outSizeNet;
|
|
std::cerr << "TcpStream::recvPkt() CC: ";
|
|
std::cerr << " iWS: " << inWinSize;
|
|
std::cerr << " aRead: " << availRead;
|
|
std::cerr << " iAck: " << inAckno;
|
|
std::cerr << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "TcpStream::recv() Not Connected";
|
|
std::cerr << std::endl;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::recv() ReadPkt(" << size << ")" << std::endl;
|
|
//std::cerr << printPkt(input, size);
|
|
//std::cerr << std::endl;
|
|
#endif
|
|
TcpPacket *pkt = new TcpPacket();
|
|
if (0 < pkt -> readPacket(input, size))
|
|
{
|
|
lastIncomingPkt = getCurrentTS();
|
|
handleIncoming(pkt);
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::recv() Bad Packet Deleting!";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
delete pkt;
|
|
}
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return;
|
|
}
|
|
|
|
|
|
int TcpStream::tick()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
//std::cerr << "TcpStream::tick()" << std::endl;
|
|
recv_check(); /* recv is async */
|
|
send();
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool TcpStream::getRemoteAddress(struct sockaddr_in &raddr)
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
if (peerKnown)
|
|
{
|
|
raddr = peeraddr;
|
|
}
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
|
|
return peerKnown;
|
|
}
|
|
|
|
uint8 TcpStream::TcpState()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
uint8 err = state;
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
|
|
return err;
|
|
}
|
|
|
|
int TcpStream::TcpErrorState()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
int err = errorState;
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
/********************* SOME EXPOSED DEBUGGING FNS ******************/
|
|
|
|
static int ilevel = 100;
|
|
|
|
bool TcpStream::widle()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
/* init */
|
|
if (!lastWriteTF)
|
|
{
|
|
lastWriteTF = int_wbytes();
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return false;
|
|
}
|
|
|
|
if ((lastWriteTF == int_wbytes()) && (inSize == 0) && inQueue.empty())
|
|
{
|
|
wcount++;
|
|
if (wcount > ilevel)
|
|
{
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return true;
|
|
}
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return false;
|
|
}
|
|
wcount = 0;
|
|
lastWriteTF = int_wbytes();
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return false;
|
|
}
|
|
|
|
|
|
bool TcpStream::ridle()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
/* init */
|
|
if (!lastReadTF)
|
|
{
|
|
lastReadTF = int_rbytes();
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return false;
|
|
}
|
|
|
|
if ((lastReadTF == int_rbytes()) && (outSizeRead + outQueue.size() + outSizeNet== 0))
|
|
{
|
|
rcount++;
|
|
if (rcount > ilevel)
|
|
{
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return true;
|
|
}
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return false;
|
|
}
|
|
rcount = 0;
|
|
lastReadTF = int_rbytes();
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return false;
|
|
}
|
|
|
|
uint32 TcpStream::wbytes()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
uint32 wb = int_wbytes();
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return wb;
|
|
}
|
|
|
|
uint32 TcpStream::rbytes()
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
uint32 rb = int_rbytes();
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
return rb;
|
|
}
|
|
|
|
/********************* ALL BELOW HERE IS INTERNAL ******************
|
|
******************* AND ALWAYS PROTECTED BY A MUTEX ***************/
|
|
|
|
int TcpStream::recv_check()
|
|
{
|
|
double cts = getCurrentTS(); // fractional seconds.
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
if (state > TCP_SYN_RCVD)
|
|
{
|
|
int availRead = outSizeRead + outQueue.size() * MAX_SEG + outSizeNet;
|
|
std::cerr << "TcpStream::recv_check() CC: ";
|
|
std::cerr << " iWS: " << inWinSize;
|
|
std::cerr << " aRead: " << availRead;
|
|
std::cerr << " iAck: " << inAckno;
|
|
std::cerr << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "TcpStream::recv_check() Not Connected";
|
|
std::cerr << std::endl;
|
|
}
|
|
#endif
|
|
|
|
// make sure we've rcvd something!
|
|
if ((state > TCP_SYN_RCVD) &&
|
|
(cts - lastIncomingPkt > kNoPktTimeout))
|
|
{
|
|
/* shut it all down */
|
|
/* this period should be equivalent
|
|
* to the firewall timeouts ???
|
|
*
|
|
* for max efficiency
|
|
*/
|
|
|
|
#ifdef DEBUG_TCP_STREAM_CLOSE
|
|
std::cerr << "TcpStream::recv_check() state = CLOSED (NoPktTimeout)";
|
|
std::cerr << std::endl;
|
|
dumpstate_locked(std::cerr);
|
|
#endif
|
|
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_CLOSED (kNoPktTimeout)");
|
|
|
|
outStreamActive = false;
|
|
inStreamActive = false;
|
|
state = TCP_CLOSED;
|
|
cleanup();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int TcpStream::cleanup()
|
|
{
|
|
// This shuts it all down! no matter what.
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::cleanup() state = TCP_CLOSED");
|
|
|
|
outStreamActive = false;
|
|
inStreamActive = false;
|
|
state = TCP_CLOSED;
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream STATE -> TCP_CLOSED" << std::endl;
|
|
#endif
|
|
|
|
//peerKnown = false; //??? NOT SURE -> for a rapid reconnetion this might be key??
|
|
|
|
/* reset TTL */
|
|
setTTL(TCP_STD_TTL);
|
|
|
|
// clear arrays.
|
|
inSize = 0;
|
|
while(inQueue.size() > 0)
|
|
{
|
|
dataBuffer *db = inQueue.front();
|
|
inQueue.pop_front();
|
|
delete db;
|
|
}
|
|
|
|
while(outPkt.size() > 0)
|
|
{
|
|
TcpPacket *pkt = outPkt.front();
|
|
outPkt.pop_front();
|
|
delete pkt;
|
|
}
|
|
|
|
|
|
// clear arrays.
|
|
outSizeRead = 0;
|
|
outSizeNet = 0;
|
|
while(outQueue.size() > 0)
|
|
{
|
|
dataBuffer *db = outQueue.front();
|
|
outQueue.pop_front();
|
|
delete db;
|
|
}
|
|
|
|
while(inPkt.size() > 0)
|
|
{
|
|
TcpPacket *pkt = inPkt.front();
|
|
inPkt.pop_front();
|
|
delete pkt;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int TcpStream::handleIncoming(TcpPacket *pkt)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::handleIncoming()" << std::endl;
|
|
#endif
|
|
switch(state)
|
|
{
|
|
case TCP_CLOSED:
|
|
case TCP_LISTEN:
|
|
/* if receive SYN
|
|
* -> respond SYN/ACK
|
|
* To State: SYN_RCVD
|
|
*
|
|
* else Discard.
|
|
*/
|
|
return incoming_Closed(pkt);
|
|
break;
|
|
case TCP_SYN_SENT:
|
|
/* if receive SYN
|
|
* -> respond SYN/ACK
|
|
* To State: SYN_RCVD
|
|
*
|
|
* if receive SYN+ACK
|
|
* -> respond ACK
|
|
* To State: TCP_ESTABLISHED
|
|
*
|
|
* else Discard.
|
|
*/
|
|
return incoming_SynSent(pkt);
|
|
break;
|
|
case TCP_SYN_RCVD:
|
|
/* if receive ACK
|
|
* To State: TCP_ESTABLISHED
|
|
*/
|
|
return incoming_SynRcvd(pkt);
|
|
break;
|
|
case TCP_ESTABLISHED:
|
|
/* if receive FIN
|
|
* -> respond ACK
|
|
* To State: TCP_CLOSE_WAIT
|
|
* else Discard.
|
|
*/
|
|
return incoming_Established(pkt);
|
|
break;
|
|
case TCP_FIN_WAIT_1:
|
|
/* state entered by close() call.
|
|
* if receive FIN
|
|
* -> respond ACK
|
|
* To State: TCP_CLOSING
|
|
*
|
|
* if receive ACK
|
|
* -> no response
|
|
* To State: TCP_FIN_WAIT_2
|
|
*
|
|
* if receive FIN+ACK
|
|
* -> respond ACK
|
|
* To State: TCP_TIMED_WAIT
|
|
*
|
|
*/
|
|
return incoming_Established(pkt);
|
|
//return incoming_FinWait1(pkt);
|
|
break;
|
|
case TCP_FIN_WAIT_2:
|
|
/* if receive FIN
|
|
* -> respond ACK
|
|
* To State: TCP_TIMED_WAIT
|
|
*/
|
|
return incoming_Established(pkt);
|
|
//return incoming_FinWait2(pkt);
|
|
break;
|
|
case TCP_CLOSING:
|
|
/* if receive ACK
|
|
* To State: TCP_TIMED_WAIT
|
|
*/
|
|
/* all handled in Established */
|
|
return incoming_Established(pkt);
|
|
//return incoming_Closing(pkt);
|
|
break;
|
|
case TCP_CLOSE_WAIT:
|
|
/*
|
|
* wait for our close to be called.
|
|
*/
|
|
/* all handled in Established */
|
|
return incoming_Established(pkt);
|
|
//return incoming_CloseWait(pkt);
|
|
break;
|
|
case TCP_LAST_ACK:
|
|
/* entered by the local close() after sending FIN.
|
|
* if receive ACK
|
|
* To State: TCP_CLOSED
|
|
*/
|
|
/* all handled in Established */
|
|
return incoming_Established(pkt);
|
|
/*
|
|
return incoming_LastAck(pkt);
|
|
*/
|
|
|
|
break;
|
|
/* this is actually the only
|
|
* final state where packets not expected!
|
|
*/
|
|
case TCP_TIMED_WAIT:
|
|
/* State: TCP_TIMED_WAIT
|
|
*
|
|
* discard all -> both connections FINed
|
|
* timeout of this state.
|
|
*
|
|
*/
|
|
|
|
#ifdef DEBUG_TCP_STREAM_CLOSE
|
|
std::cerr << "TcpStream::handleIncoming() state = CLOSED (TimedWait)";
|
|
std::cerr << std::endl;
|
|
dumpstate_locked(std::cerr);
|
|
#endif
|
|
|
|
state = TCP_CLOSED;
|
|
// return incoming_TimedWait(pkt);
|
|
{
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_CLOSED (recvd TCP_TIMED_WAIT?)");
|
|
}
|
|
break;
|
|
}
|
|
delete pkt;
|
|
return 1;
|
|
}
|
|
|
|
|
|
int TcpStream::incoming_Closed(TcpPacket *pkt)
|
|
{
|
|
/* if receive SYN
|
|
* -> respond SYN/ACK
|
|
* To State: SYN_RCVD
|
|
*
|
|
* else Discard.
|
|
*/
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::incoming_Closed()" << std::endl;
|
|
#endif
|
|
if ((pkt -> hasSyn()) && (!pkt -> hasAck()))
|
|
{
|
|
/* Init Connection */
|
|
|
|
/* save seqno */
|
|
initPeerSeqno = pkt -> seqno;
|
|
inAckno = initPeerSeqno + 1;
|
|
outWinSize = pkt -> winsize;
|
|
|
|
|
|
inWinSize = maxWinSize;
|
|
|
|
/* we can get from SynSent as well,
|
|
* but only send one SYN packet
|
|
*/
|
|
|
|
/* start packet */
|
|
TcpPacket *rsp = new TcpPacket();
|
|
|
|
if (state == TCP_CLOSED)
|
|
{
|
|
outSeqno = genSequenceNo();
|
|
initOurSeqno = outSeqno;
|
|
outAcked = outSeqno; /* min - 1 expected */
|
|
|
|
/* setup Congestion Charging */
|
|
congestThreshold = TCP_MAX_WIN;
|
|
congestWinSize = MAX_SEG;
|
|
congestUpdate = outAcked + congestWinSize;
|
|
|
|
rsp -> setSyn();
|
|
}
|
|
|
|
rsp -> setAck(inAckno);
|
|
/* seq + winsize set in toSend() */
|
|
|
|
/* as we have received something ... we can up the TTL */
|
|
setTTL(TCP_STD_TTL);
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::incoming_Closed() Sending reply" << std::endl;
|
|
std::cerr << "SeqNo: " << rsp->seqno << " Ack: " << rsp->ackno;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
toSend(rsp);
|
|
/* change state */
|
|
state = TCP_SYN_RCVD;
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream STATE -> TCP_SYN_RCVD" << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_SYN_RECVD (recvd SYN & !ACK)");
|
|
}
|
|
|
|
delete pkt;
|
|
return 1;
|
|
}
|
|
|
|
|
|
int TcpStream::incoming_SynSent(TcpPacket *pkt)
|
|
{
|
|
/* if receive SYN
|
|
* -> respond SYN/ACK
|
|
* To State: SYN_RCVD
|
|
*
|
|
* if receive SYN+ACK
|
|
* -> respond ACK
|
|
* To State: TCP_ESTABLISHED
|
|
*
|
|
* else Discard.
|
|
*/
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::incoming_SynSent()" << std::endl;
|
|
#endif
|
|
|
|
if ((pkt -> hasSyn()) && (pkt -> hasAck()))
|
|
{
|
|
/* check stuff */
|
|
if (pkt -> getAck() != outSeqno)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::incoming_SynSent() Bad Ack - Deleting " << std::endl;
|
|
#endif
|
|
/* bad ignore */
|
|
delete pkt;
|
|
return -1;
|
|
}
|
|
|
|
/* Complete Connection */
|
|
|
|
/* save seqno */
|
|
initPeerSeqno = pkt -> seqno;
|
|
inAckno = initPeerSeqno + 1;
|
|
|
|
outWinSize = pkt -> winsize;
|
|
|
|
outAcked = pkt -> getAck();
|
|
|
|
/* before ACK, reset the TTL
|
|
* As they have sent something, and we have received
|
|
* through the firewall, set to STD.
|
|
*/
|
|
setTTL(TCP_STD_TTL);
|
|
|
|
/* ack the Syn Packet */
|
|
sendAck();
|
|
|
|
/* change state */
|
|
state = TCP_ESTABLISHED;
|
|
outStreamActive = true;
|
|
inStreamActive = true;
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream STATE -> TCP_ESTABLISHED" << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_ESTABLISHED (recvd SUN & ACK)");
|
|
|
|
delete pkt;
|
|
}
|
|
else /* same as if closed! (simultaneous open) */
|
|
{
|
|
return incoming_Closed(pkt);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
int TcpStream::incoming_SynRcvd(TcpPacket *pkt)
|
|
{
|
|
/* if receive ACK
|
|
* To State: TCP_ESTABLISHED
|
|
*/
|
|
if (pkt -> hasRst())
|
|
{
|
|
/* trouble */
|
|
state = TCP_CLOSED;
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream STATE -> TCP_CLOSED" << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_CLOSED (recvd RST)");
|
|
|
|
delete pkt;
|
|
return 1;
|
|
}
|
|
|
|
bool ackWithData = false;
|
|
|
|
if (pkt -> hasAck())
|
|
{
|
|
if (pkt -> hasSyn())
|
|
{
|
|
/* has resent syn -> check it matches */
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "incoming_SynRcvd -> Pkt with ACK + SYN" << std::endl;
|
|
#endif
|
|
}
|
|
|
|
/* check stuff */
|
|
if (pkt -> getAck() != outSeqno)
|
|
{
|
|
/* bad ignore */
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "incoming_SynRcvd -> Ignoring Pkt with bad ACK" << std::endl;
|
|
#endif
|
|
delete pkt;
|
|
return -1;
|
|
}
|
|
|
|
/* Complete Connection */
|
|
|
|
/* save seqno */
|
|
if (pkt -> datasize > 0)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::incoming_SynRcvd() ACK with Data!" << std::endl;
|
|
std::cerr << "TcpStream::incoming_SynRcvd() Shoudn't recv ... unless initACK lost!" << std::endl;
|
|
#endif
|
|
// managed to trigger this under windows...
|
|
// perhaps the initial Ack was lost,
|
|
// believe we should just pass this packet
|
|
// directly to the incoming_Established... once
|
|
// the following has been done.
|
|
// and it should all work!
|
|
//exit(1);
|
|
ackWithData = true;
|
|
}
|
|
|
|
inAckno = pkt -> seqno; /* + pkt -> datasize; */
|
|
outWinSize = pkt -> winsize;
|
|
|
|
outAcked = pkt -> getAck();
|
|
|
|
|
|
/* As they have sent something, and we have received
|
|
* through the firewall, set to STD.
|
|
*/
|
|
setTTL(TCP_STD_TTL);
|
|
|
|
/* change state */
|
|
state = TCP_ESTABLISHED;
|
|
outStreamActive = true;
|
|
inStreamActive = true;
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream STATE -> TCP_ESTABLISHED" << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_ESTABLISHED (have SYN, recvd ACK)");
|
|
}
|
|
|
|
if (ackWithData)
|
|
{
|
|
/* connection Established -> handle normally */
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "incoming_SynRcvd -> Handling Data with Ack Pkt!";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
incoming_Established(pkt);
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "incoming_SynRcvd -> Ignoring Pkt!" << std::endl;
|
|
#endif
|
|
/* else nothing */
|
|
delete pkt;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int TcpStream::incoming_Established(TcpPacket *pkt)
|
|
{
|
|
/* first handle the Ack ...
|
|
* this must be done before the queue,
|
|
* to keep the values as up-to-date as possible.
|
|
*
|
|
* must sanity check .....
|
|
* make sure that the sequence number is within the correct range.
|
|
*/
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::incoming_Established() ";
|
|
std::cerr << " Pkt->seqno: " << std::hex << pkt->seqno;
|
|
std::cerr << " Pkt->datasize: " << std::hex << pkt->datasize;
|
|
std::cerr << std::dec << std::endl;
|
|
#endif
|
|
|
|
if ((!isOldSequence(pkt->seqno, inAckno)) && // seq >= inAckno
|
|
isOldSequence(pkt->seqno, inAckno + maxWinSize)) // seq < inAckno + maxWinSize.
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::incoming_Established() valid Packet Seqno.";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
if (pkt->hasAck())
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
if (outAcked != pkt->ackno)
|
|
{
|
|
std::cerr << "TcpStream::incoming_Established() valid Packet Seqno & new Ackno.";
|
|
std::cerr << std::endl;
|
|
std::cerr << "\tUpdating OutAcked to: " << outAcked;
|
|
std::cerr << std::endl;
|
|
}
|
|
#endif
|
|
outAcked = pkt->ackno;
|
|
}
|
|
|
|
outWinSize = pkt->winsize;
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "\tUpdating OutWinSize to: " << outWinSize;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* what we do! (This is actually okay - and happens occasionally) */
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::incoming_Established() ERROR out-of-range Packet Seqno.";
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t Pkt->SeqNo: " << std::hex << pkt->seqno;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t inAckno: " << std::hex << inAckno;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t inAckno + maxWinSize: " << std::hex << inAckno + maxWinSize;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t outAcked: " << std::hex << outAcked;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t Pkt->SeqNo: " << std::hex << pkt->seqno;
|
|
std::cerr << std::dec << std::endl;
|
|
std::cerr << "\t !isOldSequence(pkt->seqno, inAckno): " << (!isOldSequence(pkt->seqno, inAckno));
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t isOldSequence(pkt->seqno, inAckno + maxWinSize): " << isOldSequence(pkt->seqno, inAckno + maxWinSize);
|
|
std::cerr << std::endl;
|
|
std::cerr << std::endl;
|
|
|
|
std::cerr << "TcpStream::incoming_Established() Sending Ack to update Peer";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
sendAck();
|
|
}
|
|
|
|
|
|
/* add to queue */
|
|
inPkt.push_back(pkt);
|
|
|
|
if (inPkt.size() > kMaxQueueSize)
|
|
{
|
|
TcpPacket *pkt = inPkt.front();
|
|
inPkt.pop_front();
|
|
delete pkt;
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::incoming_Established() inPkt reached max size...Discarding Oldest Pkt";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
}
|
|
|
|
/* use as many packets as possible */
|
|
return check_InPkts();
|
|
}
|
|
|
|
int TcpStream::check_InPkts()
|
|
{
|
|
bool found = true;
|
|
TcpPacket *pkt;
|
|
std::list<TcpPacket *>::iterator it;
|
|
while(found)
|
|
{
|
|
found = false;
|
|
for(it = inPkt.begin(); (!found) && (it != inPkt.end());)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "Checking expInAck: " << std::hex << inAckno;
|
|
std::cerr << " vs: " << std::hex << (*it)->seqno << std::dec << std::endl;
|
|
#endif
|
|
|
|
pkt = *it;
|
|
if ((*it)->seqno == inAckno)
|
|
{
|
|
//std::cerr << "\tFOUND MATCH!";
|
|
//std::cerr << std::endl;
|
|
|
|
found = true;
|
|
it = inPkt.erase(it);
|
|
|
|
}
|
|
|
|
/* see if we can discard it */
|
|
/* if smaller seqno, and not wrapping around */
|
|
else if (isOldSequence((*it)->seqno, inAckno))
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "Discarding Old Packet expAck: " << std::hex << inAckno;
|
|
std::cerr << " seqno: " << std::hex << (*it)->seqno;
|
|
std::cerr << " pkt->size: " << std::hex << (*it)->datasize;
|
|
std::cerr << " pkt->seqno+size: " << std::hex << (*it)->seqno + (*it)->datasize;
|
|
std::cerr << std::dec << std::endl;
|
|
#endif
|
|
|
|
/* discard */
|
|
it = inPkt.erase(it);
|
|
delete pkt;
|
|
|
|
}
|
|
else
|
|
{
|
|
++it;
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
if (pkt->datasize)
|
|
{
|
|
checkData(pkt->data, pkt->datasize, pkt->seqno-initPeerSeqno-1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::check_inPkts() Updating inAckno from: " << std::hex << inAckno;
|
|
#endif
|
|
|
|
/* update ack number - let it rollover */
|
|
inAckno = pkt->seqno + pkt->datasize;
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << " to: " << std::hex << inAckno;
|
|
std::cerr << std::dec << std::endl;
|
|
#endif
|
|
|
|
/* XXX This shouldn't be here, as it prevents
|
|
* the Ack being used until the packet is.
|
|
* This means that a dropped packet will stop traffic in both
|
|
* directions....
|
|
*
|
|
* Moved it to incoming_Established .... but extra
|
|
* check here to be sure!
|
|
*/
|
|
|
|
if (pkt->hasAck())
|
|
{
|
|
if (isOldSequence(outAcked, pkt->ackno))
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::check_inPkts() ERROR Ack Not Already Used!";
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t Pkt->ackno: " << std::hex << pkt->ackno;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t outAcked: " << std::hex << outAcked;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t Pkt->winsize: " << std::hex << pkt->winsize;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t outWinSize: " << std::hex << outWinSize;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\t isOldSequence(outAcked, pkt->ackno): " << isOldSequence(outAcked, pkt->ackno);
|
|
std::cerr << std::endl;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
outAcked = pkt->ackno;
|
|
outWinSize = pkt->winsize;
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "\tUpdating OutAcked to: " << outAcked;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\tUpdating OutWinSize to: " << outWinSize;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::check_inPkts() GOOD Ack Already Used!";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* push onto queue */
|
|
|
|
if (outSizeNet + pkt->datasize < MAX_SEG)
|
|
{
|
|
/* move onto outSizeNet */
|
|
if (pkt->datasize)
|
|
{
|
|
memcpy((void *) &(outDataNet[outSizeNet]), pkt->data, pkt->datasize);
|
|
outSizeNet += pkt->datasize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* if it'll overflow the buffer. */
|
|
dataBuffer *db = new dataBuffer();
|
|
|
|
/* move outDatNet -> buffer */
|
|
memcpy((void *) db->data, (void *) outDataNet, outSizeNet);
|
|
|
|
/* fill rest of space */
|
|
int remSpace = MAX_SEG - outSizeNet;
|
|
memcpy((void *) &(db->data[outSizeNet]), (void *) pkt->data, remSpace);
|
|
|
|
/* push packet onto queue */
|
|
outQueue.push_back(db);
|
|
|
|
/* any big chunks that will take up a full dataBuffer */
|
|
int remData = pkt->datasize - remSpace;
|
|
while(remData >= MAX_SEG)
|
|
{
|
|
db = new dataBuffer();
|
|
memcpy((void *) db->data, (void *) &(pkt->data[remSpace]), MAX_SEG);
|
|
|
|
remData -= MAX_SEG;
|
|
outQueue.push_back(db);
|
|
}
|
|
|
|
/* remove any remaining to outDataNet */
|
|
outSizeNet = remData;
|
|
if (outSizeNet > 0)
|
|
{
|
|
memcpy((void *) outDataNet, (void *) &(pkt->data[pkt->datasize - remData]), outSizeNet);
|
|
}
|
|
}
|
|
|
|
/* can allow more in! - update inWinSize */
|
|
UpdateInWinSize();
|
|
|
|
/* if pkt is FIN */
|
|
/* these must be here -> at the end of the reliable stream */
|
|
/* if the fin is set, ack it specially close stream */
|
|
if (pkt->hasFin())
|
|
{
|
|
/* send final ack */
|
|
sendAck();
|
|
|
|
/* closedown stream */
|
|
inStreamActive = false;
|
|
|
|
if (state == TCP_ESTABLISHED)
|
|
{
|
|
state = TCP_CLOSE_WAIT;
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::state = TCP_CLOSE_WAIT";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_CLOSE_WAIT (recvd FIN)");
|
|
}
|
|
else if (state == TCP_FIN_WAIT_1)
|
|
{
|
|
state = TCP_CLOSING;
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::state = TCP_CLOSING";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_CLOSING (FIN_WAIT_1, recvd FIN)");
|
|
}
|
|
else if (state == TCP_FIN_WAIT_2)
|
|
{
|
|
state = TCP_TIMED_WAIT;
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::state = TCP_TIMED_WAIT";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_TIMED_WAIT (FIN_WAIT_2, recvd FIN)");
|
|
|
|
cleanup();
|
|
}
|
|
}
|
|
|
|
/* if ack for our FIN */
|
|
if ((pkt->hasAck()) && (!outStreamActive)
|
|
&& (pkt->ackno == outSeqno))
|
|
{
|
|
if (state == TCP_FIN_WAIT_1)
|
|
{
|
|
state = TCP_FIN_WAIT_2;
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::state = TCP_FIN_WAIT_2";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_FIN_WAIT_2 (FIN_WAIT_1, recvd ACK)");
|
|
}
|
|
else if (state == TCP_LAST_ACK)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM_CLOSE
|
|
std::cerr << "TcpStream::state = TCP_CLOSED (LastAck)";
|
|
std::cerr << std::endl;
|
|
dumpstate_locked(std::cerr);
|
|
#endif
|
|
|
|
state = TCP_CLOSED;
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_CLOSED (LAST_ACK, recvd ACK)");
|
|
|
|
cleanup();
|
|
}
|
|
else if (state == TCP_CLOSING)
|
|
{
|
|
state = TCP_TIMED_WAIT;
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::state = TCP_TIMED_WAIT";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_TIMED_WAIT (TCP_CLOSING, recvd ACK)");
|
|
|
|
cleanup();
|
|
}
|
|
}
|
|
|
|
delete pkt;
|
|
|
|
} /* end of found */
|
|
} /* while(found) */
|
|
return 1;
|
|
}
|
|
|
|
/* This Fn should be called after each read, or recvd data (thats added to the buffer)
|
|
*/
|
|
int TcpStream::UpdateInWinSize()
|
|
{
|
|
/* InWinSize = maxWinSze - QueuedData,
|
|
* actually we can allow a lot more to queue up...
|
|
* inWinSize = 65536, unless QueuedData > 65536.
|
|
* inWinSize = 2 * maxWinSize - QueuedData;
|
|
*
|
|
*/
|
|
|
|
uint32 queuedData = int_read_pending();
|
|
if (queuedData < maxWinSize)
|
|
{
|
|
inWinSize = maxWinSize;
|
|
}
|
|
else if (queuedData < 2 * maxWinSize)
|
|
{
|
|
inWinSize = 2 * maxWinSize - queuedData;
|
|
}
|
|
else
|
|
{
|
|
inWinSize = 0;
|
|
}
|
|
return inWinSize;
|
|
}
|
|
|
|
int TcpStream::sendAck()
|
|
{
|
|
/* simple -> toSend fills in ack/winsize
|
|
* and the rest is history
|
|
*/
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::sendAck()";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
return toSend(new TcpPacket(), false);
|
|
}
|
|
|
|
void TcpStream::setRemoteAddress(const struct sockaddr_in &raddr)
|
|
{
|
|
peeraddr = raddr;
|
|
peerKnown = true;
|
|
}
|
|
|
|
|
|
int TcpStream::toSend(TcpPacket *pkt, bool retrans)
|
|
{
|
|
int outPktSize = MAX_SEG + TCP_PSEUDO_HDR_SIZE;
|
|
char tmpOutPkt[outPktSize];
|
|
|
|
if (!peerKnown)
|
|
{
|
|
/* Major Error! */
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::toSend() peerUnknown ERROR!!!";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
delete pkt;
|
|
return 0;
|
|
}
|
|
|
|
/* get accurate timestamp */
|
|
double cts = getCurrentTS();
|
|
|
|
pkt -> winsize = inWinSize;
|
|
pkt -> seqno = outSeqno;
|
|
|
|
/* increment seq no */
|
|
if (pkt->datasize)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
checkData(pkt->data, pkt->datasize, outSeqno-initOurSeqno-1);
|
|
#endif
|
|
outSeqno += pkt->datasize;
|
|
}
|
|
|
|
if (pkt->hasSyn())
|
|
{
|
|
/* should not have data! */
|
|
if (pkt->datasize)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "SYN Packet shouldn't contain data!" << std::endl;
|
|
#endif
|
|
}
|
|
outSeqno++;
|
|
}
|
|
else
|
|
{
|
|
/* cannot auto Ack SynPackets */
|
|
pkt -> setAck(inAckno);
|
|
}
|
|
|
|
pkt -> winsize = inWinSize;
|
|
|
|
/* store old info */
|
|
lastSentAck = pkt -> ackno;
|
|
lastSentWinSize = pkt -> winsize;
|
|
keepAliveTimer = cts;
|
|
|
|
pkt -> writePacket(tmpOutPkt, outPktSize);
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::toSend() Seqno: ";
|
|
std::cerr << pkt->seqno << " size: " << pkt->datasize;
|
|
std::cerr << " Ackno: ";
|
|
std::cerr << pkt->ackno << " winsize: " << pkt->winsize;
|
|
std::cerr << std::endl;
|
|
//std::cerr << printPkt(tmpOutPkt, outPktSize) << std::endl;
|
|
#endif
|
|
|
|
udp -> sendPkt(tmpOutPkt, outPktSize, peeraddr, ttl);
|
|
|
|
if (retrans)
|
|
{
|
|
/* restart timers */
|
|
pkt -> ts = cts;
|
|
pkt -> retrans = 0;
|
|
|
|
startRetransmitTimer();
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::toSend() Adding to outPkt --> Seqno: ";
|
|
std::cerr << pkt->seqno << " size: " << pkt->datasize;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
outPkt.push_back(pkt);
|
|
}
|
|
else
|
|
{
|
|
delete pkt;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* single retransmit timer.
|
|
*
|
|
*/
|
|
void TcpStream::startRetransmitTimer()
|
|
{
|
|
if (retransTimerOn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
retransTimerTs = getCurrentTS();
|
|
retransTimerOn = true;
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::startRetransmitTimer() peer: " << peeraddr;
|
|
std::cerr << " retransTimeout: " << retransTimeout;
|
|
std::cerr << " retransTimerTs: " << std::setprecision(12) <<retransTimerTs;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
void TcpStream::restartRetransmitTimer()
|
|
{
|
|
stopRetransmitTimer();
|
|
startRetransmitTimer();
|
|
}
|
|
|
|
|
|
void TcpStream::stopRetransmitTimer()
|
|
{
|
|
if (!retransTimerOn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::stopRetransmitTimer() peer: " << peeraddr;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
retransTimerOn = false;
|
|
}
|
|
|
|
|
|
void TcpStream::resetRetransmitTimer()
|
|
{
|
|
retransTimerOn = false;
|
|
retransTimeout = 2.0 * (rtt_est + 4.0 * rtt_dev);
|
|
|
|
// happens too often for RETRANS debugging.
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::resetRetransmitTimer() peer: " << peeraddr;
|
|
std::cerr << " retransTimeout: " << std::setprecision(12) << retransTimeout;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
}
|
|
|
|
void TcpStream::incRetransmitTimeout()
|
|
{
|
|
retransTimeout = 2 * retransTimeout;
|
|
if (retransTimeout > TCP_RETRANS_MAX_TIMEOUT)
|
|
{
|
|
retransTimeout = TCP_RETRANS_MAX_TIMEOUT;
|
|
}
|
|
|
|
#ifdef DEBUG_TCP_STREAM_RETRANS
|
|
std::cerr << "TcpStream::incRetransmitTimer() peer: " << peeraddr;
|
|
std::cerr << " retransTimeout: " << std::setprecision(12) << retransTimeout;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int TcpStream::retrans()
|
|
{
|
|
int outPktSize = MAX_SEG + TCP_PSEUDO_HDR_SIZE;
|
|
char tmpOutPkt[outPktSize];
|
|
|
|
if (!peerKnown)
|
|
{
|
|
/* Major Error! */
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::retrans() peerUnknown ERROR!!!";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (!retransTimerOn)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
double cts = getCurrentTS();
|
|
if (cts - retransTimerTs < retransTimeout)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (outPkt.begin() == outPkt.end())
|
|
{
|
|
resetRetransmitTimer();
|
|
return 0;
|
|
}
|
|
|
|
TcpPacket *pkt = outPkt.front();
|
|
|
|
if (!pkt)
|
|
{
|
|
/* error */
|
|
return 0;
|
|
}
|
|
|
|
/* retransmission -> adjust the congestWinSize and congestThreshold
|
|
*/
|
|
|
|
congestThreshold = congestWinSize / 2;
|
|
congestWinSize = MAX_SEG;
|
|
congestUpdate = outAcked + congestWinSize; // point when we can up the winSize.
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::retrans() Adjusting Congestion Parameters: ";
|
|
std::cerr << std::endl;
|
|
std::cerr << "\tcongestWinSize: " << congestWinSize;
|
|
std::cerr << " congestThreshold: " << congestThreshold;
|
|
std::cerr << " congestUpdate: " << congestUpdate;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
/* update ackno and winsize */
|
|
if (!(pkt->hasSyn()))
|
|
{
|
|
pkt->setAck(inAckno);
|
|
lastSentAck = pkt -> ackno;
|
|
}
|
|
|
|
pkt->winsize = inWinSize;
|
|
lastSentWinSize = pkt -> winsize;
|
|
|
|
keepAliveTimer = cts;
|
|
|
|
pkt->writePacket(tmpOutPkt, outPktSize);
|
|
|
|
#ifdef DEBUG_TCP_STREAM_RETRANS
|
|
std::cerr << "TcpStream::retrans()";
|
|
std::cerr << " peer: " << peeraddr;
|
|
std::cerr << " hasSyn: " << pkt->hasSyn();
|
|
std::cerr << " Seqno: ";
|
|
std::cerr << pkt->seqno << " size: " << pkt->datasize;
|
|
std::cerr << " Ackno: ";
|
|
std::cerr << pkt->ackno << " winsize: " << pkt->winsize;
|
|
std::cerr << " retrans: " << (int) pkt->retrans;
|
|
std::cerr << " timeout: " << std::setprecision(12) << retransTimeout;
|
|
std::cerr << std::endl;
|
|
|
|
//std::cerr << printPkt(tmpOutPkt, outPktSize) << std::endl;
|
|
#endif
|
|
|
|
/* if its a syn packet ** thats been
|
|
* transmitting for a while, maybe
|
|
* we should increase the ttl.
|
|
*/
|
|
|
|
if ((pkt->hasSyn()) && (getTTL() < TCP_STD_TTL))
|
|
{
|
|
/* calculate a new TTL */
|
|
if (mTTL_end > cts)
|
|
{
|
|
setTTL(TCP_DEFAULT_FIREWALL_TTL);
|
|
}
|
|
else
|
|
{
|
|
setTTL(getTTL() + 1);
|
|
}
|
|
|
|
std::string out;
|
|
rs_sprintf(out, "TcpStream::retrans() Startup SYNs retrans count: %u New TTL: %d", pkt->retrans, getTTL());
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, out);
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << out.str() << std::endl;
|
|
#endif
|
|
}
|
|
|
|
/* catch excessive retransmits
|
|
* - Allow Syn case more....
|
|
* - if not SYN or TTL has reached STD then timeout quickly.
|
|
|
|
* OLD 2nd Logic (below) has been replaced with lower logic.
|
|
* (((!pkt->hasSyn()) || (TCP_STD_TTL == getTTL()))
|
|
* && (pkt->retrans > kMaxPktRetransmit)))
|
|
* Problem was that the retransmit of Syn packet had STD_TTL, and was triggering Close (and SeqNo change).
|
|
* It seemed to work anyway.... But might cause coonnection failures. Will reduce the MaxSyn Retransmit
|
|
* so something more reasonable as well.
|
|
* ((!pkt->hasSyn()) && (pkt->retrans > kMaxPktRetransmit)))
|
|
*/
|
|
|
|
if ((pkt->hasSyn() && (pkt->retrans > kMaxSynPktRetransmit)) ||
|
|
((!pkt->hasSyn()) && (pkt->retrans > kMaxPktRetransmit)))
|
|
|
|
{
|
|
|
|
/* too many attempts close stream */
|
|
#ifdef DEBUG_TCP_STREAM_CLOSE
|
|
std::cerr << "TcpStream::retrans() Too many Retransmission Attempts (";
|
|
std::cerr << (int) pkt->retrans << ") for Peer: " << peeraddr << std::endl;
|
|
std::cerr << "TcpStream::retrans() Closing Socket Connection";
|
|
std::cerr << std::endl;
|
|
|
|
//dumpPacket(std::cerr, (unsigned char *) tmpOutPkt, outPktSize);
|
|
dumpstate_locked(std::cerr);
|
|
#endif
|
|
|
|
rslog(RSL_WARNING,rstcpstreamzone,"TcpStream::state => TCP_CLOSED (Too Many Retransmits)");
|
|
|
|
outStreamActive = false;
|
|
inStreamActive = false;
|
|
state = TCP_CLOSED;
|
|
cleanup();
|
|
return 0;
|
|
}
|
|
|
|
|
|
udp -> sendPkt(tmpOutPkt, outPktSize, peeraddr, ttl);
|
|
|
|
/* restart timers */
|
|
pkt->ts = cts;
|
|
pkt->retrans++;
|
|
|
|
/*
|
|
* finally - double the retransTimeout ... (Karn's Algorithm)
|
|
* except if we are starting a connection... i.e. hasSyn()
|
|
*/
|
|
|
|
if (!pkt->hasSyn())
|
|
{
|
|
incRetransmitTimeout();
|
|
restartRetransmitTimer();
|
|
}
|
|
else
|
|
{
|
|
resetRetransmitTimer();
|
|
startRetransmitTimer();
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
void TcpStream::acknowledge()
|
|
{
|
|
/* cleans up acknowledge packets */
|
|
/* packets are pushed back in order */
|
|
std::list<TcpPacket *>::iterator it;
|
|
double cts = getCurrentTS();
|
|
bool updateRTT = true;
|
|
bool clearedPkts = false;
|
|
|
|
for(it = outPkt.begin(); (it != outPkt.end()) &&
|
|
(isOldSequence((*it)->seqno, outAcked));
|
|
it = outPkt.erase(it))
|
|
{
|
|
TcpPacket *pkt = (*it);
|
|
clearedPkts = true;
|
|
|
|
/* adjust the congestWinSize and congestThreshold
|
|
* congestUpdate <= outAcked
|
|
*
|
|
***/
|
|
|
|
if (!isOldSequence(outAcked, congestUpdate))
|
|
{
|
|
if (congestWinSize < congestThreshold)
|
|
{
|
|
/* double it baby! */
|
|
congestWinSize *= 2;
|
|
}
|
|
else
|
|
{
|
|
/* linear increase */
|
|
congestWinSize += MAX_SEG;
|
|
}
|
|
|
|
if (congestWinSize > maxWinSize)
|
|
{
|
|
congestWinSize = maxWinSize;
|
|
}
|
|
|
|
congestUpdate = outAcked + congestWinSize; // point when we can up the winSize.
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::acknowledge() Adjusting Congestion Parameters: ";
|
|
std::cerr << std::endl;
|
|
std::cerr << "\tcongestWinSize: " << congestWinSize;
|
|
std::cerr << " congestThreshold: " << congestThreshold;
|
|
std::cerr << " congestUpdate: " << congestUpdate;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
}
|
|
|
|
|
|
/* update the RoundTripTime,
|
|
* using Jacobson's values.
|
|
* RTT = a RTT + (1-a) M
|
|
* where
|
|
* RTT is RoundTripTime estimate.
|
|
* a = 7/8,
|
|
* M = time for ack.
|
|
*
|
|
* D = a D + (1 - a) | RTT - M |
|
|
* where
|
|
* D is approx Deviation.
|
|
* a,RTT & M are the same as above.
|
|
*
|
|
* Timeout = RTT + 4 * D.
|
|
*
|
|
* And Karn's Algorithm...
|
|
* which says
|
|
* (1) do not update RTT or D for retransmitted packets.
|
|
* + the ones that follow .... (the ones whos ack was
|
|
* delayed by the retranmission)
|
|
* (2) double timeout, when packets fail. (done in retrans).
|
|
*/
|
|
|
|
if (pkt->retrans)
|
|
{
|
|
updateRTT = false;
|
|
}
|
|
|
|
if (updateRTT) /* can use for RTT calc */
|
|
{
|
|
double ack_time = cts - pkt->ts;
|
|
rtt_est = RTT_ALPHA * rtt_est + (1.0 - RTT_ALPHA) * ack_time;
|
|
rtt_dev = RTT_ALPHA * rtt_dev + (1.0 - RTT_ALPHA) * fabs(rtt_est - ack_time);
|
|
retransTimeout = rtt_est + 4.0 * rtt_dev;
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::acknowledge() Updating RTT: ";
|
|
std::cerr << std::endl;
|
|
std::cerr << "\tAckTime: " << ack_time;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\tRRT_est: " << rtt_est;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\tRTT_dev: " << rtt_dev;
|
|
std::cerr << std::endl;
|
|
std::cerr << "\tTimeout: " << retransTimeout;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
}
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
else
|
|
{
|
|
std::cerr << "TcpStream::acknowledge() Not Updating RTT for retransmitted Pkt Sequence";
|
|
std::cerr << std::endl;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::acknowledge() Removing Seqno: ";
|
|
std::cerr << pkt->seqno << " size: " << pkt->datasize;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
delete pkt;
|
|
}
|
|
|
|
/* This is triggered if we have recieved acks for retransmitted packets....
|
|
* In this case we want to reset the timeout, and remove the doubling.
|
|
*
|
|
* If we don't do this, and there have been more dropped packets,
|
|
* the the timeout gets continually doubled. which will virtually stop
|
|
* all communication.
|
|
*
|
|
* This will effectively trigger the retransmission of the next dropped packet.
|
|
*/
|
|
|
|
/*
|
|
* if have acked all data - resetRetransTimer()
|
|
*/
|
|
|
|
if (it == outPkt.end())
|
|
{
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::acknowledge() peer: " << peeraddr;
|
|
std::cerr << " Backlog cleared, resetRetransmitTimer";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
resetRetransmitTimer();
|
|
}
|
|
else if (clearedPkts)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::acknowledge() peer: " << peeraddr;
|
|
std::cerr << " Cleared some packets -> resetRetransmitTimer + start";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
resetRetransmitTimer();
|
|
startRetransmitTimer();
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
int TcpStream::send()
|
|
{
|
|
/* handle network interface always */
|
|
/* clean up as much as possible */
|
|
acknowledge();
|
|
/* send any old packets */
|
|
retrans();
|
|
|
|
|
|
if (state < TCP_ESTABLISHED)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/* get the inQueue, can send */
|
|
|
|
|
|
/* determine exactly how much we can send */
|
|
uint32 maxsend = congestWinSize;
|
|
uint32 inTransit;
|
|
|
|
if (outWinSize < congestWinSize)
|
|
{
|
|
maxsend = outWinSize;
|
|
}
|
|
|
|
if (outSeqno < outAcked)
|
|
{
|
|
inTransit = (TCP_MAX_SEQ - outAcked) + outSeqno;
|
|
}
|
|
else
|
|
{
|
|
inTransit = outSeqno - outAcked;
|
|
}
|
|
|
|
if (maxsend > inTransit)
|
|
{
|
|
maxsend -= inTransit;
|
|
}
|
|
else
|
|
{
|
|
maxsend = 0;
|
|
}
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
int availSend = inQueue.size() * MAX_SEG + inSize;
|
|
std::cerr << "TcpStream::send() CC: ";
|
|
std::cerr << "oWS: " << outWinSize;
|
|
std::cerr << " cWS: " << congestWinSize;
|
|
std::cerr << " | inT: " << inTransit;
|
|
std::cerr << " mSnd: " << maxsend;
|
|
std::cerr << " aSnd: " << availSend;
|
|
std::cerr << " | oSeq: " << outSeqno;
|
|
std::cerr << " oAck: " << outAcked;
|
|
std::cerr << " cUpd: " << congestUpdate;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
int sent = 0;
|
|
while((inQueue.size() > 0) && (maxsend >= MAX_SEG))
|
|
{
|
|
dataBuffer *db = inQueue.front();
|
|
inQueue.pop_front();
|
|
|
|
TcpPacket *pkt = new TcpPacket(db->data, MAX_SEG);
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::send() Segment ===> Seqno: ";
|
|
std::cerr << pkt->seqno << " size: " << pkt->datasize;
|
|
std::cerr << std::endl;
|
|
#endif
|
|
sent++;
|
|
maxsend -= MAX_SEG;
|
|
toSend(pkt);
|
|
delete db;
|
|
}
|
|
|
|
/* if inqueue empty, and enough window space, send partial stuff */
|
|
if ((!sent) && (inQueue.empty()) && (maxsend >= inSize) && (inSize))
|
|
{
|
|
TcpPacket *pkt = new TcpPacket(inData, inSize);
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::send() Remaining ===>";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
inSize = 0;
|
|
sent++;
|
|
maxsend -= inSize;
|
|
toSend(pkt);
|
|
}
|
|
|
|
/* if send nothing */
|
|
bool needsAck = false;
|
|
|
|
if (!sent)
|
|
{
|
|
double cts = getCurrentTS();
|
|
/* if needs ack */
|
|
if (isOldSequence(lastSentAck,inAckno))
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::send() Ack Triggered (Ackno)";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
needsAck = true;
|
|
}
|
|
|
|
/* if needs window
|
|
* if added enough space for packet, or
|
|
* (this case is equivalent to persistence timer)
|
|
* haven't sent anything for a while, and the
|
|
* window size has drastically increased.
|
|
* */
|
|
if (((lastSentWinSize < MAX_SEG) && (inWinSize > MAX_SEG)) ||
|
|
((cts - keepAliveTimer > retransTimeout * 4) &&
|
|
(inWinSize > lastSentWinSize + 4 * MAX_SEG)))
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::send() Ack Triggered (Window)";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
needsAck = true;
|
|
}
|
|
|
|
/* if needs keepalive */
|
|
if (cts - keepAliveTimer > keepAliveTimeout)
|
|
{
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::send() Ack Triggered (KAlive)";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
needsAck = true;
|
|
}
|
|
|
|
|
|
/* if end of stream -> switch mode -> send fin (with ack) */
|
|
if ((!outStreamActive) && (inQueue.size() + inSize == 0) &&
|
|
((state == TCP_ESTABLISHED) || (state == TCP_CLOSE_WAIT)))
|
|
{
|
|
/* finish the stream */
|
|
TcpPacket *pkt = new TcpPacket();
|
|
pkt -> setFin();
|
|
|
|
needsAck = false;
|
|
toSend(pkt, false);
|
|
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::send() Fin Triggered";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
if (state == TCP_ESTABLISHED)
|
|
{
|
|
state = TCP_FIN_WAIT_1;
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::state = TCP_FIN_WAIT_1";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_FIN_WAIT_1 (End of Stream)");
|
|
}
|
|
else if (state == TCP_CLOSE_WAIT)
|
|
{
|
|
state = TCP_LAST_ACK;
|
|
#ifdef DEBUG_TCP_STREAM
|
|
std::cerr << "TcpStream::state = TCP_LAST_ACK";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
rslog(RSL_WARNING, rstcpstreamzone, "TcpStream::state => TCP_LAST_ACK (CLOSE_WAIT, End of Stream)");
|
|
}
|
|
|
|
}
|
|
|
|
if (needsAck)
|
|
{
|
|
sendAck();
|
|
}
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
else
|
|
{
|
|
std::cerr << "TcpStream::send() No Ack";
|
|
std::cerr << std::endl;
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
else
|
|
{
|
|
std::cerr << "TcpStream::send() Stuff Sent";
|
|
std::cerr << std::endl;
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
|
|
uint32 TcpStream::genSequenceNo()
|
|
{
|
|
return RSRandom::random_u32();
|
|
//return 1000; // TCP_MAX_SEQ - 1000; //1000; //(rand() - 100000) + time(NULL) % 100000;
|
|
//return (rand() - 100000) + time(NULL) % 100000;
|
|
|
|
}
|
|
|
|
|
|
bool TcpStream::isOldSequence(uint32 tst, uint32 curr)
|
|
{
|
|
return ((int)((tst)-(curr)) < 0);
|
|
|
|
std::cerr << "TcpStream::isOldSequence(): Case ";
|
|
/* if tst < curr */
|
|
if ((int)((tst)-(curr)) < 0)
|
|
{
|
|
if (curr - tst < TCP_MAX_SEQ/2) /* diff less than half span -> old */
|
|
{
|
|
std::cerr << "1T" << std::endl;
|
|
return true;
|
|
}
|
|
std::cerr << "2F" << std::endl;
|
|
return false;
|
|
}
|
|
else if ((tst - curr) > TCP_MAX_SEQ/2)
|
|
{
|
|
std::cerr << "3T: tst-curr:" << (tst-curr) << std::endl;
|
|
return true;
|
|
}
|
|
std::cerr << "4F: tst-curr:" << (tst-curr) << std::endl;
|
|
return false;
|
|
}
|
|
|
|
#ifdef WINDOWS_SYS
|
|
#include <time.h>
|
|
#include <sys/timeb.h>
|
|
#endif
|
|
|
|
// Little fn to get current timestamp in an independent manner.
|
|
static double getCurrentTS()
|
|
{
|
|
|
|
#ifndef WINDOWS_SYS
|
|
struct timeval cts_tmp;
|
|
gettimeofday(&cts_tmp, NULL);
|
|
double cts = (cts_tmp.tv_sec) + ((double) cts_tmp.tv_usec) / 1000000.0;
|
|
#else
|
|
struct _timeb timebuf;
|
|
_ftime( &timebuf);
|
|
double cts = (timebuf.time) + ((double) timebuf.millitm) / 1000.0;
|
|
#endif
|
|
return cts;
|
|
}
|
|
|
|
|
|
|
|
uint32 TcpStream::int_wbytes()
|
|
{
|
|
return outSeqno - initOurSeqno - 1;
|
|
}
|
|
|
|
uint32 TcpStream::int_rbytes()
|
|
{
|
|
return inAckno - initPeerSeqno - 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
/********* Special debugging stuff *****/
|
|
|
|
#ifdef DEBUG_TCP_STREAM_EXTRA
|
|
|
|
#include <stdio.h>
|
|
|
|
static FILE *bc_fd = 0;
|
|
int setupBinaryCheck(std::string fname)
|
|
{
|
|
bc_fd = RsDirUtil::rs_fopen(fname.c_str(), "r");
|
|
return 1;
|
|
}
|
|
|
|
/* uses seq number to track position -> ensure no rollover */
|
|
int checkData(uint8 *data, int size, int idx)
|
|
{
|
|
if (bc_fd <= 0)
|
|
{
|
|
return -1;
|
|
}
|
|
std::cerr << "checkData(" << idx << "+" << size << ")";
|
|
|
|
int tmpsize = size;
|
|
uint8 tmpdata[tmpsize];
|
|
if (-1 == fseek(bc_fd, idx, SEEK_SET))
|
|
{
|
|
std::cerr << "Fseek Issues!" << std::endl;
|
|
exit(1);
|
|
return -1;
|
|
}
|
|
|
|
if (1 != fread(tmpdata, tmpsize, 1, bc_fd))
|
|
{
|
|
std::cerr << "Length Difference!" << std::endl;
|
|
exit(1);
|
|
return -1;
|
|
}
|
|
|
|
for(int i = 0; i < size; i++)
|
|
{
|
|
if (data[i] != tmpdata[i])
|
|
{
|
|
std::cerr << "Byte Difference!" << std::endl;
|
|
exit(1);
|
|
return -1;
|
|
}
|
|
}
|
|
std::cerr << "OK" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/***** Dump state of TCP Stream - to workout why it was closed ****/
|
|
int TcpStream::dumpstate_locked(std::ostream &out)
|
|
{
|
|
out << "TcpStream::dumpstate()";
|
|
out << "=======================================================";
|
|
|
|
out << std::endl;
|
|
out << "state: " << (int) state;
|
|
out << " errorState: " << (int) errorState;
|
|
out << std::endl;
|
|
|
|
out << "(Streams) inStreamActive: " << inStreamActive;
|
|
out << " outStreamActive: " << outStreamActive;
|
|
out << std::endl;
|
|
|
|
out << "(Timeouts) maxWinSize: " << maxWinSize;
|
|
out << " keepAliveTimeout: " << keepAliveTimeout;
|
|
out << " retransTimeout: " << retransTimeout;
|
|
out << std::endl;
|
|
|
|
out << "(Timers) keepAliveTimer: " << std::setprecision(12) << keepAliveTimer;
|
|
out << " lastIncomingPkt: " << std::setprecision(12) << lastIncomingPkt;
|
|
out << std::endl;
|
|
|
|
out << "(Tracking) lastSendAck: " << lastSentAck;
|
|
out << " lastSendWinSize: " << lastSentWinSize;
|
|
out << std::endl;
|
|
|
|
out << "(Init) initOutSeqno: " << initOurSeqno;
|
|
out << " initPeerSeqno: " << initPeerSeqno;
|
|
out << std::endl;
|
|
|
|
out << "(r/w) lastWriteTF: " << lastWriteTF;
|
|
out << " lastReadTF: " << lastReadTF;
|
|
out << " wcount: " << wcount;
|
|
out << " rcount: " << rcount;
|
|
out << std::endl;
|
|
|
|
out << "(rtt) rtt_est: " << rtt_est;
|
|
out << " rtt_dev: " << rtt_dev;
|
|
out << std::endl;
|
|
|
|
out << "(congestion) congestThreshold: " << congestThreshold;
|
|
out << " congestWinSize: " << congestWinSize;
|
|
out << " congestUpdate: " << congestUpdate;
|
|
out << std::endl;
|
|
|
|
out << "(TTL) mTTL_period: " << mTTL_period;
|
|
out << " mTTL_start: " << std::setprecision(12) << mTTL_start;
|
|
out << " mTTL_end: " << std::setprecision(12) << mTTL_end;
|
|
out << std::endl;
|
|
|
|
out << "(Peer) peerKnown: " << peerKnown;
|
|
out << " peerAddr: " << peeraddr;
|
|
out << std::endl;
|
|
|
|
out << "-------------------------------------------------------";
|
|
out << std::endl;
|
|
status_locked(out);
|
|
out << "=======================================================";
|
|
out << std::endl;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int TcpStream::dumpstate(std::ostream &out)
|
|
{
|
|
tcpMtx.lock(); /********** LOCK MUTEX *********/
|
|
|
|
dumpstate_locked(out);
|
|
|
|
tcpMtx.unlock(); /******** UNLOCK MUTEX *********/
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int dumpPacket(std::ostream &out, unsigned char *pkt, uint32_t size)
|
|
{
|
|
uint32_t i;
|
|
|
|
out << "dumpPacket() Size: " << size;
|
|
out << std::endl;
|
|
out << "------------------------------------------------------";
|
|
for(i = 0; i < size; i++)
|
|
{
|
|
if (i % 16 == 0)
|
|
{
|
|
out << std::endl;
|
|
}
|
|
out << std::hex << std::setfill('0') << std::setw(2) << (int) pkt[i] << ":";
|
|
}
|
|
if ((i - 1) % 16 != 0)
|
|
{
|
|
out << std::endl;
|
|
}
|
|
out << "------------------------------------------------------";
|
|
out << std::dec << std::endl;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|