TCP now acks incoming data, and stores it into the receive buffer. It should also reassemble packets received out-of-order, but this is untested. These bytes don't get to the application layer yet, mostly because i'm unsure how this is supposed to happen.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19163 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -61,17 +61,21 @@ private:
|
|||||||
|
|
||||||
class TCPSegment {
|
class TCPSegment {
|
||||||
public:
|
public:
|
||||||
TCPSegment(uint32 sequenceNumber, uint32 size, bigtime_t timeout);
|
|
||||||
|
struct list_link link;
|
||||||
|
|
||||||
|
TCPSegment(net_buffer *buffer, uint32 sequenceNumber, bigtime_t timeout);
|
||||||
~TCPSegment();
|
~TCPSegment();
|
||||||
|
|
||||||
|
net_buffer *fBuffer;
|
||||||
bigtime_t fTime;
|
bigtime_t fTime;
|
||||||
uint32 fSequenceNumber;
|
uint32 fSequenceNumber;
|
||||||
uint32 fAcknowledgementNumber;
|
|
||||||
net_timer fTimer;
|
net_timer fTimer;
|
||||||
bool fTimedOut;
|
bool fTimedOut;
|
||||||
};
|
};
|
||||||
|
|
||||||
status_t Send(uint16 flags, bool empty);
|
status_t SendQueuedData(uint16 flags, bool empty);
|
||||||
|
status_t EnqueueReceivedData(net_buffer *buffer, uint32 sequenceNumber);
|
||||||
status_t Reset(uint32 sequenceNum, uint32 acknowledgeNum);
|
status_t Reset(uint32 sequenceNum, uint32 acknowledgeNum);
|
||||||
static void TimeWait(struct net_timer *timer, void *data);
|
static void TimeWait(struct net_timer *timer, void *data);
|
||||||
|
|
||||||
@@ -88,6 +92,9 @@ private:
|
|||||||
net_buffer *fSendBuffer;
|
net_buffer *fSendBuffer;
|
||||||
net_buffer *fReceiveBuffer;
|
net_buffer *fReceiveBuffer;
|
||||||
|
|
||||||
|
struct list fReorderQueue;
|
||||||
|
struct list fWaitQueue;
|
||||||
|
|
||||||
TCPConnection *fHashLink;
|
TCPConnection *fHashLink;
|
||||||
tcp_state fState;
|
tcp_state fState;
|
||||||
status_t fError;
|
status_t fError;
|
||||||
@@ -98,15 +105,17 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
TCPConnection::TCPSegment::TCPSegment(uint32 sequenceNumber, uint32 size, bigtime_t timeout)
|
TCPConnection::TCPSegment::TCPSegment(net_buffer *buffer, uint32 sequenceNumber, bigtime_t timeout)
|
||||||
:
|
:
|
||||||
|
fBuffer(buffer),
|
||||||
fTime(system_time()),
|
fTime(system_time()),
|
||||||
fSequenceNumber(sequenceNumber),
|
fSequenceNumber(sequenceNumber),
|
||||||
fAcknowledgementNumber(sequenceNumber+size),
|
|
||||||
fTimedOut(false)
|
fTimedOut(false)
|
||||||
{
|
{
|
||||||
sStackModule->init_timer(&fTimer, &TCPConnection::ResendSegment, this);
|
if (timeout > 0) {
|
||||||
sStackModule->set_timer(&fTimer, timeout);
|
sStackModule->init_timer(&fTimer, &TCPConnection::ResendSegment, this);
|
||||||
|
sStackModule->set_timer(&fTimer, timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -133,6 +142,8 @@ TCPConnection::TCPConnection(net_socket *socket)
|
|||||||
{
|
{
|
||||||
benaphore_init(&fLock, "TCPConnection");
|
benaphore_init(&fLock, "TCPConnection");
|
||||||
sStackModule->init_timer(&fTimer, TimeWait, this);
|
sStackModule->init_timer(&fTimer, TimeWait, this);
|
||||||
|
list_init(&fReorderQueue);
|
||||||
|
list_init(&fWaitQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -173,12 +184,12 @@ TCPConnection::Close()
|
|||||||
nextState = FIN_WAIT1;
|
nextState = FIN_WAIT1;
|
||||||
if (fState == CLOSE_WAIT)
|
if (fState == CLOSE_WAIT)
|
||||||
nextState = LAST_ACK;
|
nextState = LAST_ACK;
|
||||||
status_t status = Send(TCP_FLG_FIN | TCP_FLG_ACK, false);
|
status_t status = SendQueuedData(TCP_FLG_FIN | TCP_FLG_ACK, false);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
fState = nextState;
|
fState = nextState;
|
||||||
TRACE(("TCP: %p.Close(): Entering state %d\n", this, fState));
|
TRACE(("TCP: %p.Close(): Entering state %d\n", this, fState));
|
||||||
// need to wait until fState returns to CLOSED
|
//do i need to wait until fState returns to CLOSED?
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +276,7 @@ TCPConnection::Connect(const struct sockaddr *address)
|
|||||||
|
|
||||||
TRACE(("TCP: Connect(): starting 3-way handshake...\n"));
|
TRACE(("TCP: Connect(): starting 3-way handshake...\n"));
|
||||||
// send SYN
|
// send SYN
|
||||||
status = Send(TCP_FLG_SYN, false);
|
status = SendQueuedData(TCP_FLG_SYN, false);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
fState = SYN_SENT;
|
fState = SYN_SENT;
|
||||||
@@ -370,13 +381,13 @@ TCPConnection::SendData(net_buffer *buffer)
|
|||||||
if (fSendBuffer == NULL) {
|
if (fSendBuffer == NULL) {
|
||||||
fSendBuffer = buffer;
|
fSendBuffer = buffer;
|
||||||
fNextByteToWrite += bufSize;
|
fNextByteToWrite += bufSize;
|
||||||
return Send(TCP_FLG_ACK, false);
|
return SendQueuedData(TCP_FLG_ACK, false);
|
||||||
} else {
|
} else {
|
||||||
status_t status = sBufferModule->merge(fSendBuffer, buffer, true);
|
status_t status = sBufferModule->merge(fSendBuffer, buffer, true);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
fNextByteToWrite += bufSize;
|
fNextByteToWrite += bufSize;
|
||||||
return Send(TCP_FLG_ACK, false);
|
return SendQueuedData(TCP_FLG_ACK, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,10 +417,28 @@ TCPConnection::SendAvailable()
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
TCPConnection::ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer)
|
TCPConnection::ReadData(size_t numBytes, uint32 flags, net_buffer **buffer)
|
||||||
{
|
{
|
||||||
TRACE(("TCP:%p.ReadData()\n", this));
|
TRACE(("TCP:%p.ReadData()\n", this));
|
||||||
return B_ERROR;
|
|
||||||
|
BenaphoreLocker lock(&fLock);
|
||||||
|
|
||||||
|
// must be in a synchronous state
|
||||||
|
if (fState != ESTABLISHED || fState != FIN_WAIT1 || fState != FIN_WAIT2) {
|
||||||
|
// is this correct semantics?
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fError != B_OK)
|
||||||
|
return fError;
|
||||||
|
|
||||||
|
if (fReceiveBuffer->size < numBytes)
|
||||||
|
numBytes = fReceiveBuffer->size;
|
||||||
|
*buffer = sBufferModule->split(fReceiveBuffer, numBytes);
|
||||||
|
if (*buffer != NULL)
|
||||||
|
return B_OK;
|
||||||
|
else
|
||||||
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -425,6 +454,75 @@ TCPConnection::ReadAvailable()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
TCPConnection::EnqueueReceivedData(net_buffer *buffer, uint32 sequenceNumber)
|
||||||
|
{
|
||||||
|
TRACE(("TCP:%p.EnqueueReceivedData(%p, %u)\n", this, buffer, sequenceNumber));
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
if (sequenceNumber == fNextByteExpected) {
|
||||||
|
// first check if the received buffer meets up with the first
|
||||||
|
// segment in the ReorderQueue
|
||||||
|
TCPSegment *next;
|
||||||
|
while ((next = (TCPSegment *)list_get_first_item(&fReorderQueue)) != NULL) {
|
||||||
|
if (sequenceNumber + buffer->size >= next->fSequenceNumber) {
|
||||||
|
if (sequenceNumber + buffer->size > next->fSequenceNumber) {
|
||||||
|
status = sBufferModule->trim(buffer, sequenceNumber - next->fSequenceNumber);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
status = sBufferModule->merge(buffer, next->fBuffer, true);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
list_remove_item(&fReorderQueue, next);
|
||||||
|
delete next;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fNextByteExpected += buffer->size;
|
||||||
|
if (fReceiveBuffer == NULL)
|
||||||
|
fReceiveBuffer = buffer;
|
||||||
|
else {
|
||||||
|
status = sBufferModule->merge(fReceiveBuffer, buffer, true);
|
||||||
|
if (status < B_OK) {
|
||||||
|
fNextByteExpected -= buffer->size;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// add this buffer into the ReorderQueue in the correct place
|
||||||
|
// creating a new TCPSegment if necessary
|
||||||
|
TCPSegment *next = NULL;
|
||||||
|
do {
|
||||||
|
next = (TCPSegment *)list_get_next_item(&fReorderQueue, next);
|
||||||
|
if (next != NULL && next->fSequenceNumber < sequenceNumber)
|
||||||
|
continue;
|
||||||
|
if (next != NULL && sequenceNumber + buffer->size >= next->fSequenceNumber) {
|
||||||
|
// merge the new buffer with the next buffer
|
||||||
|
if (sequenceNumber + buffer->size > next->fSequenceNumber) {
|
||||||
|
status = sBufferModule->trim(buffer, sequenceNumber - next->fSequenceNumber);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
status = sBufferModule->merge(buffer, next->fBuffer, true);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
next->fBuffer = buffer;
|
||||||
|
next->fSequenceNumber = sequenceNumber;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
TCPSegment *segment = new(std::nothrow) TCPSegment(buffer, sequenceNumber, -1);
|
||||||
|
if (next == NULL)
|
||||||
|
list_add_item(&fReorderQueue, segment);
|
||||||
|
else
|
||||||
|
list_insert_item_before(&fReorderQueue, next, segment);
|
||||||
|
} while (next != NULL);
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
TCPConnection::ReceiveData(net_buffer *buffer)
|
TCPConnection::ReceiveData(net_buffer *buffer)
|
||||||
{
|
{
|
||||||
@@ -442,7 +540,8 @@ TCPConnection::ReceiveData(net_buffer *buffer)
|
|||||||
status_t status = B_OK;
|
status_t status = B_OK;
|
||||||
uint32 byteAckd = ntohl(header.acknowledge_num);
|
uint32 byteAckd = ntohl(header.acknowledge_num);
|
||||||
uint32 byteRcvd = ntohl(header.sequence_num);
|
uint32 byteRcvd = ntohl(header.sequence_num);
|
||||||
uint32 payloadLength = buffer->size - ((uint32)header.header_length << 2);
|
uint32 headerLength = (uint32)header.header_length << 2;
|
||||||
|
uint32 payloadLength = buffer->size - headerLength;
|
||||||
|
|
||||||
TRACE(("TCP: ReceiveData(): Connection in state %d received packet %p with flags %X!\n", fState, buffer, header.flags));
|
TRACE(("TCP: ReceiveData(): Connection in state %d received packet %p with flags %X!\n", fState, buffer, header.flags));
|
||||||
switch (fState) {
|
switch (fState) {
|
||||||
@@ -483,11 +582,6 @@ TCPConnection::ReceiveData(net_buffer *buffer)
|
|||||||
nextState = SYN_RCVD;
|
nextState = SYN_RCVD;
|
||||||
flags |= TCP_FLG_SYN;
|
flags |= TCP_FLG_SYN;
|
||||||
}
|
}
|
||||||
status = Send(flags, false);
|
|
||||||
if (status == B_OK)
|
|
||||||
fState = nextState;
|
|
||||||
else
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SYN_RCVD:
|
case SYN_RCVD:
|
||||||
@@ -553,18 +647,35 @@ TCPConnection::ReceiveData(net_buffer *buffer)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fState != CLOSING && fState != LAST_ACK)
|
flags |= TCP_FLG_ACK;
|
||||||
status = Send(flags | TCP_FLG_ACK, false);
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
fState = nextState;
|
|
||||||
} else {
|
} else {
|
||||||
// out-of-order packet received. remind the other side of where we are
|
// out-of-order packet received. remind the other side of where we are
|
||||||
return Send(TCP_FLG_ACK, true);
|
return SendQueuedData(TCP_FLG_ACK, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
TRACE(("TCP %p.ReceiveData():Entering state %d\n", this, fState));
|
TRACE(("TCP %p.ReceiveData():Entering state %d\n", this, fState));
|
||||||
|
|
||||||
|
// state machine is done switching states and the data is good.
|
||||||
|
// put it in the receive buffer
|
||||||
|
// TODO: This isn't the most efficient way to do it, and will need to be changed
|
||||||
|
// to deal with Silly Window Syndrome
|
||||||
|
bufferHeader.Remove(headerLength);
|
||||||
|
if (buffer->size > 0) {
|
||||||
|
status = EnqueueReceivedData(buffer, byteRcvd);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
} else
|
||||||
|
sBufferModule->free(buffer);
|
||||||
|
|
||||||
|
if (fState != CLOSING && fState != LAST_ACK) {
|
||||||
|
status = SendQueuedData(flags, false);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
fState = nextState;
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,9 +722,9 @@ TCPConnection::ResendSegment(struct net_timer *timer, void *data)
|
|||||||
The fLock benaphore must be held before calling.
|
The fLock benaphore must be held before calling.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
TCPConnection::Send(uint16 flags, bool empty)
|
TCPConnection::SendQueuedData(uint16 flags, bool empty)
|
||||||
{
|
{
|
||||||
TRACE(("TCP:%p.Send(%X,%s)\n", this, flags, empty ? "1" : "0"));
|
TRACE(("TCP:%p.SendQueuedData(%X,%s)\n", this, flags, empty ? "1" : "0"));
|
||||||
|
|
||||||
if (fRoute == NULL)
|
if (fRoute == NULL)
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
@@ -635,9 +746,9 @@ TCPConnection::Send(uint16 flags, bool empty)
|
|||||||
|
|
||||||
sAddressModule->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address);
|
sAddressModule->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address);
|
||||||
sAddressModule->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer);
|
sAddressModule->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer);
|
||||||
TRACE(("TCP:%p.Send() to address %s\n", this,
|
TRACE(("TCP:%p.SendQueuedData() to address %s\n", this,
|
||||||
AddressString(sDomain, (sockaddr *)&buffer->destination, true).Data()));
|
AddressString(sDomain, (sockaddr *)&buffer->destination, true).Data()));
|
||||||
TRACE(("TCP:%p.Send() from address %s\n", this,
|
TRACE(("TCP:%p.SendQueuedData() from address %s\n", this,
|
||||||
AddressString(sDomain, (sockaddr *)&buffer->source, true).Data()));
|
AddressString(sDomain, (sockaddr *)&buffer->source, true).Data()));
|
||||||
|
|
||||||
uint16 advWin = TCP_MAX_RECV_BUF - (fNextByteExpected - fNextByteToRead);
|
uint16 advWin = TCP_MAX_RECV_BUF - (fNextByteExpected - fNextByteToRead);
|
||||||
|
|||||||
Reference in New Issue
Block a user