* Big rework of TCP - doesn't have any additional features yet, though, even
though it can now connect to itself (and should, in theory, also be able to connect to other TCP implemenations). * Separated the big state machine from ReceiveData() into several smaller sized methods. * Moved reset and acknowledgement handling for all kind of receives to a common place in tcp_receive_data(). * Removed locking for now - the previous locking approach was wrong due to a number of reasons: the previous version deadlocked when it had to connect locally, and it also didn't take into account that TCP is a full-duplex protocol; it's not wise to only be able to manage one direction at a time if that's not really needed. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19329 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -86,38 +86,59 @@ tcp_segment::~tcp_segment()
|
|||||||
|
|
||||||
TCPConnection::TCPConnection(net_socket *socket)
|
TCPConnection::TCPConnection(net_socket *socket)
|
||||||
:
|
:
|
||||||
fLastByteAckd(0), //system_time()),
|
fLastAcknowledged(0), //system_time()),
|
||||||
fNextByteToSend(fLastByteAckd),
|
fSendNext(fLastAcknowledged),
|
||||||
fNextByteToWrite(fLastByteAckd + 1),
|
fSendWindow(0),
|
||||||
fNextByteToRead(0),
|
|
||||||
fNextByteExpected(0),
|
|
||||||
fLastByteReceived(0),
|
|
||||||
fAvgRTT(TCP_INITIAL_RTT),
|
|
||||||
fSendBuffer(NULL),
|
fSendBuffer(NULL),
|
||||||
|
fRoute(NULL),
|
||||||
|
fReceiveNext(0),
|
||||||
|
fReceiveWindow(0),
|
||||||
|
fAvgRTT(TCP_INITIAL_RTT),
|
||||||
fReceiveBuffer(NULL),
|
fReceiveBuffer(NULL),
|
||||||
fState(CLOSED),
|
fState(CLOSED),
|
||||||
fError(B_OK),
|
fError(B_OK)
|
||||||
fRoute(NULL)
|
|
||||||
{
|
{
|
||||||
benaphore_init(&fLock, "TCPConnection");
|
|
||||||
gStackModule->init_timer(&fTimer, _TimeWait, this);
|
gStackModule->init_timer(&fTimer, _TimeWait, this);
|
||||||
list_init(&fReorderQueue);
|
list_init(&fReorderQueue);
|
||||||
list_init(&fWaitQueue);
|
list_init(&fWaitQueue);
|
||||||
|
|
||||||
|
benaphore_init(&fReceiveLock, "tcp receive");
|
||||||
|
benaphore_init(&fSendLock, "tcp send");
|
||||||
|
fAcceptSemaphore = create_sem(0, "tcp accept");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TCPConnection::~TCPConnection()
|
TCPConnection::~TCPConnection()
|
||||||
{
|
{
|
||||||
benaphore_destroy(&fLock);
|
gStackModule->set_timer(&fTimer, -1);
|
||||||
|
|
||||||
|
benaphore_destroy(&fReceiveLock);
|
||||||
|
benaphore_destroy(&fSendLock);
|
||||||
|
delete_sem(fAcceptSemaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
TCPConnection::InitCheck() const
|
||||||
|
{
|
||||||
|
if (fReceiveLock.sem < B_OK)
|
||||||
|
return fReceiveLock.sem;
|
||||||
|
if (fSendLock.sem < B_OK)
|
||||||
|
return fSendLock.sem;
|
||||||
|
if (fAcceptSemaphore < B_OK)
|
||||||
|
return fAcceptSemaphore;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
TCPConnection::_IsAcknowledgeValid(uint32 acknowledge)
|
TCPConnection::_IsAcknowledgeValid(uint32 acknowledge)
|
||||||
{
|
{
|
||||||
return fLastByteAckd > fNextByteToSend
|
dprintf("last byte ackd %lu, next byte to send %lu, ack %lu\n", fLastAcknowledged, fSendNext, acknowledge);
|
||||||
? acknowledge >= fLastByteAckd || acknowledge <= fNextByteToSend
|
return fLastAcknowledged > fSendNext
|
||||||
: acknowledge >= fLastByteAckd && acknowledge <= fNextByteToSend;
|
? acknowledge >= fLastAcknowledged || acknowledge <= fSendNext
|
||||||
|
: acknowledge >= fLastAcknowledged && acknowledge <= fSendNext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -126,16 +147,16 @@ TCPConnection::_IsSequenceValid(uint32 sequence, uint32 length)
|
|||||||
{
|
{
|
||||||
uint32 end = sequence + length;
|
uint32 end = sequence + length;
|
||||||
|
|
||||||
if (fNextByteToRead < fNextByteToRead + TCP_MAX_RECV_BUF) {
|
if (fReceiveNext < fReceiveNext + TCP_MAX_RECV_BUF) {
|
||||||
return sequence >= fNextByteToRead
|
return sequence >= fReceiveNext
|
||||||
&& end <= fNextByteToRead + TCP_MAX_RECV_BUF;
|
&& end <= fReceiveNext + TCP_MAX_RECV_BUF;
|
||||||
}
|
}
|
||||||
|
|
||||||
// integer overflow
|
// integer overflow
|
||||||
return (sequence >= fNextByteToRead
|
return (sequence >= fReceiveNext
|
||||||
|| sequence <= fNextByteToRead + TCP_MAX_RECV_BUF)
|
|| sequence <= fReceiveNext + TCP_MAX_RECV_BUF)
|
||||||
&& (end >= fNextByteToRead
|
&& (end >= fReceiveNext
|
||||||
|| end <= fNextByteToRead + TCP_MAX_RECV_BUF);
|
|| end <= fReceiveNext + TCP_MAX_RECV_BUF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -143,15 +164,7 @@ status_t
|
|||||||
TCPConnection::Open()
|
TCPConnection::Open()
|
||||||
{
|
{
|
||||||
TRACE(("%p.Open()\n", this));
|
TRACE(("%p.Open()\n", this));
|
||||||
if (gAddressModule == NULL)
|
// nothing to do here...
|
||||||
return B_ERROR;
|
|
||||||
TRACE(("TCP: Open(): Using Address Module %p\n", gAddressModule));
|
|
||||||
|
|
||||||
BenaphoreLocker lock(&fLock);
|
|
||||||
gAddressModule->set_to_empty_address((sockaddr *)&socket->address);
|
|
||||||
gAddressModule->set_port((sockaddr *)&socket->address, 0);
|
|
||||||
gAddressModule->set_to_empty_address((sockaddr *)&socket->peer);
|
|
||||||
gAddressModule->set_port((sockaddr *)&socket->peer, 0);
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,23 +172,28 @@ TCPConnection::Open()
|
|||||||
status_t
|
status_t
|
||||||
TCPConnection::Close()
|
TCPConnection::Close()
|
||||||
{
|
{
|
||||||
BenaphoreLocker lock(&fLock);
|
BenaphoreLocker lock(&fSendLock);
|
||||||
TRACE(("TCP:%p.Close()\n", this));
|
TRACE(("TCP:%p.Close()\n", this));
|
||||||
if (fState == SYNCHRONIZE_SENT || fState == LISTEN) {
|
if (fState == SYNCHRONIZE_SENT || fState == LISTEN) {
|
||||||
fState = CLOSED;
|
fState = CLOSED;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
tcp_state nextState = CLOSED;
|
|
||||||
|
tcp_state previousState = fState;
|
||||||
|
|
||||||
if (fState == SYNCHRONIZE_RECEIVED || fState == ESTABLISHED)
|
if (fState == SYNCHRONIZE_RECEIVED || fState == ESTABLISHED)
|
||||||
nextState = FINISH_SENT;
|
fState = FINISH_SENT;
|
||||||
if (fState == FINISH_RECEIVED)
|
else if (fState == FINISH_RECEIVED)
|
||||||
nextState = WAIT_FOR_FINISH_ACKNOWLEDGE;
|
fState = WAIT_FOR_FINISH_ACKNOWLEDGE;
|
||||||
|
else
|
||||||
|
fState = CLOSED;
|
||||||
|
|
||||||
status_t status = _SendQueuedData(TCP_FLAG_FINISH | TCP_FLAG_ACKNOWLEDGE, false);
|
status_t status = _SendQueuedData(TCP_FLAG_FINISH | TCP_FLAG_ACKNOWLEDGE, false);
|
||||||
if (status != B_OK)
|
if (status != B_OK) {
|
||||||
|
fState = previousState;
|
||||||
return status;
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
fState = nextState;
|
|
||||||
TRACE(("TCP: %p.Close(): Entering state %d\n", this, fState));
|
TRACE(("TCP: %p.Close(): Entering state %d\n", this, fState));
|
||||||
// TODO: do i need to wait until fState returns to CLOSED?
|
// TODO: do i need to wait until fState returns to CLOSED?
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -187,14 +205,8 @@ TCPConnection::Free()
|
|||||||
{
|
{
|
||||||
TRACE(("TCP:%p.Free()\n", this));
|
TRACE(("TCP:%p.Free()\n", this));
|
||||||
|
|
||||||
BenaphoreLocker hashLock(&gConnectionLock);
|
// TODO: if this connection is not in the hash, we don't have to call this one
|
||||||
BenaphoreLocker lock(&fLock);
|
remove_connection(this);
|
||||||
tcp_connection_key key;
|
|
||||||
key.local = (sockaddr *)&socket->address;
|
|
||||||
key.peer = (sockaddr *)&socket->peer;
|
|
||||||
if (hash_lookup(gConnectionHash, &key) != NULL) {
|
|
||||||
return hash_remove(gConnectionHash, (void *)this);
|
|
||||||
}
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,16 +224,16 @@ TCPConnection::Connect(const struct sockaddr *address)
|
|||||||
if (address->sa_family != AF_INET)
|
if (address->sa_family != AF_INET)
|
||||||
return EAFNOSUPPORT;
|
return EAFNOSUPPORT;
|
||||||
|
|
||||||
benaphore_lock(&gConnectionLock);
|
BenaphoreLocker lock(&fSendLock);
|
||||||
// want to release lock later, so no autolock
|
|
||||||
BenaphoreLocker lock(&fLock);
|
|
||||||
|
|
||||||
// Can only call Connect from CLOSED or LISTEN states
|
// Can only call connect() from CLOSED or LISTEN states
|
||||||
// otherwise connection is considered already connected
|
// otherwise connection is considered already connected
|
||||||
if (fState != CLOSED && fState != LISTEN) {
|
if (fState == LISTEN) {
|
||||||
benaphore_unlock(&gConnectionLock);
|
// this socket is about to connect; remove pending connections in the backlog
|
||||||
|
gSocketModule->set_max_backlog(socket, 0);
|
||||||
|
} else if (fState != CLOSED)
|
||||||
return EISCONN;
|
return EISCONN;
|
||||||
}
|
|
||||||
TRACE((" TCP: Connect(): in state %d\n", fState));
|
TRACE((" TCP: Connect(): in state %d\n", fState));
|
||||||
|
|
||||||
// get a net_route if there isn't one
|
// get a net_route if there isn't one
|
||||||
@@ -229,51 +241,36 @@ TCPConnection::Connect(const struct sockaddr *address)
|
|||||||
if (fRoute == NULL) {
|
if (fRoute == NULL) {
|
||||||
fRoute = gDatalinkModule->get_route(gDomain, (sockaddr *)address);
|
fRoute = gDatalinkModule->get_route(gDomain, (sockaddr *)address);
|
||||||
TRACE((" TCP: Connect(): Using Route %p\n", fRoute));
|
TRACE((" TCP: Connect(): Using Route %p\n", fRoute));
|
||||||
if (fRoute == NULL) {
|
if (fRoute == NULL)
|
||||||
benaphore_unlock(&gConnectionLock);
|
|
||||||
return ENETUNREACH;
|
return ENETUNREACH;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// need to associate this connection with a real address, not INADDR_ANY
|
// need to associate this connection with a real address, not INADDR_ANY
|
||||||
if (gAddressModule->is_empty_address((sockaddr *)&socket->address, false)) {
|
if (gAddressModule->is_empty_address((sockaddr *)&socket->address, false)) {
|
||||||
TRACE((" TCP: Connect(): Local Address is INADDR_ANY\n"));
|
TRACE((" TCP: Connect(): Local Address is INADDR_ANY\n"));
|
||||||
gAddressModule->set_to((sockaddr *)&socket->address, (sockaddr *)fRoute->interface->address);
|
gAddressModule->set_to((sockaddr *)&socket->address, (sockaddr *)fRoute->interface->address);
|
||||||
// since most stacks terminate connections from port 0
|
|
||||||
// use port 40000 for now. This should be moved to Bind(), and Bind() called before Connect().
|
|
||||||
gAddressModule->set_port((sockaddr *)&socket->address, htons(40000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gAddressModule->set_to((sockaddr *)&socket->peer, address);
|
||||||
|
|
||||||
// make sure connection does not already exist
|
// make sure connection does not already exist
|
||||||
tcp_connection_key key;
|
status_t status = insert_connection(this);
|
||||||
key.local = (sockaddr *)&socket->address;
|
if (status < B_OK) {
|
||||||
key.peer = address;
|
TRACE((" TCP: Connect(): could not add connection: %s!\n", strerror(status)));
|
||||||
if (hash_lookup(gConnectionHash, &key) != NULL) {
|
|
||||||
benaphore_unlock(&gConnectionLock);
|
|
||||||
return EADDRINUSE;
|
|
||||||
}
|
|
||||||
TRACE(("TCP: Connect(): connecting...\n"));
|
|
||||||
|
|
||||||
status_t status;
|
|
||||||
gAddressModule->set_to((sockaddr *)&socket->peer, address);
|
|
||||||
status = hash_insert(gConnectionHash, (void *)this);
|
|
||||||
if (status != B_OK) {
|
|
||||||
TRACE(("TCP: Connect(): Error inserting connection into hash!\n"));
|
|
||||||
benaphore_unlock(&gConnectionLock);
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// done manipulating the hash, release the lock
|
|
||||||
benaphore_unlock(&gConnectionLock);
|
|
||||||
|
|
||||||
TRACE((" TCP: Connect(): starting 3-way handshake...\n"));
|
TRACE((" TCP: Connect(): starting 3-way handshake...\n"));
|
||||||
// send SYN
|
|
||||||
status = _SendQueuedData(TCP_FLAG_SYNCHRONIZE, false);
|
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
fState = SYNCHRONIZE_SENT;
|
fState = SYNCHRONIZE_SENT;
|
||||||
|
|
||||||
|
// send SYN
|
||||||
|
status = _SendQueuedData(TCP_FLAG_SYNCHRONIZE, false);
|
||||||
|
if (status != B_OK) {
|
||||||
|
fState = CLOSED;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: wait until 3-way handshake is complete
|
// TODO: wait until 3-way handshake is complete
|
||||||
TRACE((" TCP: Connect(): Connection complete\n"));
|
TRACE((" TCP: Connect(): Connection complete\n"));
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -284,7 +281,19 @@ status_t
|
|||||||
TCPConnection::Accept(struct net_socket **_acceptedSocket)
|
TCPConnection::Accept(struct net_socket **_acceptedSocket)
|
||||||
{
|
{
|
||||||
TRACE(("TCP:%p.Accept()\n", this));
|
TRACE(("TCP:%p.Accept()\n", this));
|
||||||
return B_ERROR;
|
|
||||||
|
// TODO: test for pending sockets
|
||||||
|
// TODO: test for non-blocking I/O
|
||||||
|
status_t status;
|
||||||
|
do {
|
||||||
|
status = acquire_sem(fAcceptSemaphore);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
status = gSocketModule->dequeue_connected(socket, _acceptedSocket);
|
||||||
|
} while (status < B_OK);
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -297,8 +306,11 @@ TCPConnection::Bind(sockaddr *address)
|
|||||||
if (address->sa_family != AF_INET)
|
if (address->sa_family != AF_INET)
|
||||||
return EAFNOSUPPORT;
|
return EAFNOSUPPORT;
|
||||||
|
|
||||||
BenaphoreLocker hashLock(&gConnectionLock);
|
BenaphoreLocker lock(&fSendLock);
|
||||||
BenaphoreLocker lock(&fLock);
|
// TODO: there is no lock yet for these things...
|
||||||
|
|
||||||
|
if (fState != CLOSED)
|
||||||
|
return EISCONN;
|
||||||
|
|
||||||
// let IP check whether there is an interface that supports the given address:
|
// let IP check whether there is an interface that supports the given address:
|
||||||
status_t status = next->module->bind(next, address);
|
status_t status = next->module->bind(next, address);
|
||||||
@@ -306,21 +318,18 @@ TCPConnection::Bind(sockaddr *address)
|
|||||||
return status;
|
return status;
|
||||||
|
|
||||||
gAddressModule->set_to((sockaddr *)&socket->address, address);
|
gAddressModule->set_to((sockaddr *)&socket->address, address);
|
||||||
// for now, leave port=0. TCP should still work 1 connection at a time
|
|
||||||
if (0) { //gAddressModule->get_port((sockaddr *)&socket->address) == 0) {
|
if (gAddressModule->get_port((sockaddr *)&socket->address) == 0) {
|
||||||
// assign ephemeral port
|
// assign ephemeral port
|
||||||
|
// TODO: use port 40000 and following for now
|
||||||
|
static int e = 40000;
|
||||||
|
gAddressModule->set_port((sockaddr *)&socket->address, htons(e++));
|
||||||
|
status = insert_connection(this);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Check for Socket flags
|
status = insert_connection(this);
|
||||||
tcp_connection_key key;
|
|
||||||
key.peer = (sockaddr *)&socket->peer;
|
|
||||||
key.local = (sockaddr *)&socket->address;
|
|
||||||
if (hash_lookup(gConnectionHash, &key) == NULL) {
|
|
||||||
hash_insert(gConnectionHash, (void *)this);
|
|
||||||
} else
|
|
||||||
return EADDRINUSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_OK;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -329,10 +338,10 @@ TCPConnection::Unbind(struct sockaddr *address)
|
|||||||
{
|
{
|
||||||
TRACE(("TCP:%p.Unbind()\n", this ));
|
TRACE(("TCP:%p.Unbind()\n", this ));
|
||||||
|
|
||||||
BenaphoreLocker hashLock(&gConnectionLock);
|
BenaphoreLocker lock(&fSendLock);
|
||||||
BenaphoreLocker lock(&fLock);
|
// TODO: there is no lock yet for these things...
|
||||||
|
|
||||||
status_t status = hash_remove(gConnectionHash, (void *)this);
|
status_t status = remove_connection(this);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
@@ -346,9 +355,9 @@ status_t
|
|||||||
TCPConnection::Listen(int count)
|
TCPConnection::Listen(int count)
|
||||||
{
|
{
|
||||||
TRACE(("TCP:%p.Listen()\n", this));
|
TRACE(("TCP:%p.Listen()\n", this));
|
||||||
BenaphoreLocker lock(&fLock);
|
BenaphoreLocker lock(&fSendLock);
|
||||||
if (fState != CLOSED)
|
if (fState != CLOSED)
|
||||||
return B_ERROR;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
fState = LISTEN;
|
fState = LISTEN;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -359,6 +368,7 @@ status_t
|
|||||||
TCPConnection::Shutdown(int direction)
|
TCPConnection::Shutdown(int direction)
|
||||||
{
|
{
|
||||||
TRACE(("TCP:%p.Shutdown()\n", this));
|
TRACE(("TCP:%p.Shutdown()\n", this));
|
||||||
|
// TODO: implement shutdown!
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,8 +380,8 @@ status_t
|
|||||||
TCPConnection::SendData(net_buffer *buffer)
|
TCPConnection::SendData(net_buffer *buffer)
|
||||||
{
|
{
|
||||||
TRACE(("TCP:%p.SendData()\n", this));
|
TRACE(("TCP:%p.SendData()\n", this));
|
||||||
size_t bufferSize = buffer->size;
|
|
||||||
BenaphoreLocker lock(&fLock);
|
BenaphoreLocker lock(&fSendLock);
|
||||||
if (fSendBuffer != NULL) {
|
if (fSendBuffer != NULL) {
|
||||||
status_t status = gBufferModule->merge(fSendBuffer, buffer, true);
|
status_t status = gBufferModule->merge(fSendBuffer, buffer, true);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
@@ -379,28 +389,15 @@ TCPConnection::SendData(net_buffer *buffer)
|
|||||||
} else
|
} else
|
||||||
fSendBuffer = buffer;
|
fSendBuffer = buffer;
|
||||||
|
|
||||||
fNextByteToWrite += bufferSize;
|
|
||||||
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, false);
|
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
TCPConnection::SendRoutedData(net_route *route, net_buffer *buffer)
|
|
||||||
{
|
|
||||||
TRACE(("TCP:%p.SendRoutedData()\n", this));
|
|
||||||
{
|
|
||||||
BenaphoreLocker lock(&fLock);
|
|
||||||
fRoute = route;
|
|
||||||
}
|
|
||||||
return SendData(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
TCPConnection::SendAvailable()
|
TCPConnection::SendAvailable()
|
||||||
{
|
{
|
||||||
TRACE(("TCP:%p.SendAvailable()\n", this));
|
TRACE(("TCP:%p.SendAvailable()\n", this));
|
||||||
BenaphoreLocker lock(&fLock);
|
BenaphoreLocker lock(&fSendLock);
|
||||||
if (fSendBuffer != NULL)
|
if (fSendBuffer != NULL)
|
||||||
return TCP_MAX_SEND_BUF - fSendBuffer->size;
|
return TCP_MAX_SEND_BUF - fSendBuffer->size;
|
||||||
|
|
||||||
@@ -413,7 +410,7 @@ TCPConnection::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer)
|
|||||||
{
|
{
|
||||||
TRACE(("TCP:%p.ReadData()\n", this));
|
TRACE(("TCP:%p.ReadData()\n", this));
|
||||||
|
|
||||||
BenaphoreLocker lock(&fLock);
|
BenaphoreLocker lock(&fReceiveLock);
|
||||||
|
|
||||||
// must be in a synchronous state
|
// must be in a synchronous state
|
||||||
if (fState != ESTABLISHED || fState != FINISH_SENT || fState != FINISH_ACKNOWLEDGED) {
|
if (fState != ESTABLISHED || fState != FINISH_SENT || fState != FINISH_ACKNOWLEDGED) {
|
||||||
@@ -441,7 +438,7 @@ size_t
|
|||||||
TCPConnection::ReadAvailable()
|
TCPConnection::ReadAvailable()
|
||||||
{
|
{
|
||||||
TRACE(("TCP:%p.ReadAvailable()\n", this));
|
TRACE(("TCP:%p.ReadAvailable()\n", this));
|
||||||
BenaphoreLocker lock(&fLock);
|
BenaphoreLocker lock(&fReceiveLock);
|
||||||
if (fReceiveBuffer != NULL)
|
if (fReceiveBuffer != NULL)
|
||||||
return fReceiveBuffer->size;
|
return fReceiveBuffer->size;
|
||||||
|
|
||||||
@@ -450,7 +447,7 @@ TCPConnection::ReadAvailable()
|
|||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
You must hold the connection's lock when calling this method
|
You must hold the connection's receive lock when calling this method
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
|
TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
|
||||||
@@ -458,7 +455,7 @@ TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
|
|||||||
TRACE(("TCP:%p.EnqueueReceivedData(%p, %lu)\n", this, buffer, sequence));
|
TRACE(("TCP:%p.EnqueueReceivedData(%p, %lu)\n", this, buffer, sequence));
|
||||||
status_t status;
|
status_t status;
|
||||||
|
|
||||||
if (sequence == fNextByteExpected) {
|
if (sequence == fReceiveNext) {
|
||||||
// first check if the received buffer meets up with the first
|
// first check if the received buffer meets up with the first
|
||||||
// segment in the ReorderQueue
|
// segment in the ReorderQueue
|
||||||
tcp_segment *next;
|
tcp_segment *next;
|
||||||
@@ -478,16 +475,16 @@ TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fNextByteExpected += buffer->size;
|
fReceiveNext += buffer->size;
|
||||||
if (fReceiveBuffer == NULL)
|
|
||||||
fReceiveBuffer = buffer;
|
if (fReceiveBuffer != NULL) {
|
||||||
else {
|
|
||||||
status = gBufferModule->merge(fReceiveBuffer, buffer, true);
|
status = gBufferModule->merge(fReceiveBuffer, buffer, true);
|
||||||
if (status < B_OK) {
|
if (status < B_OK) {
|
||||||
fNextByteExpected -= buffer->size;
|
fReceiveNext -= buffer->size;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
|
fReceiveBuffer = buffer;
|
||||||
} else {
|
} else {
|
||||||
// add this buffer into the ReorderQueue in the correct place
|
// add this buffer into the ReorderQueue in the correct place
|
||||||
// creating a new tcp_segment if necessary
|
// creating a new tcp_segment if necessary
|
||||||
@@ -527,195 +524,198 @@ TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
TCPConnection::ReceiveData(net_buffer *buffer)
|
TCPConnection::DelayedAcknowledge()
|
||||||
{
|
{
|
||||||
BenaphoreLocker lock(&fLock);
|
// TODO: use timer instead and/or piggyback on send
|
||||||
TRACE(("TCP:%p.ReceiveData()\n", this));
|
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, false);
|
||||||
|
}
|
||||||
|
|
||||||
NetBufferHeader<tcp_header> bufferHeader(buffer);
|
|
||||||
if (bufferHeader.Status() < B_OK)
|
|
||||||
return bufferHeader.Status();
|
|
||||||
|
|
||||||
tcp_header &header = bufferHeader.Data();
|
status_t
|
||||||
|
TCPConnection::SendAcknowledge()
|
||||||
|
{
|
||||||
|
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, false);
|
||||||
|
}
|
||||||
|
|
||||||
uint16 flags = 0x0;
|
|
||||||
tcp_state nextState = fState;
|
|
||||||
status_t status = B_OK;
|
|
||||||
uint32 byteAckd = ntohl(header.acknowledge_num);
|
|
||||||
uint32 byteRcvd = ntohl(header.sequence_num);
|
|
||||||
uint32 headerLength = (uint32)header.header_length << 2;
|
|
||||||
uint32 dataLength = buffer->size - headerLength;
|
|
||||||
|
|
||||||
TRACE(("TCP: ReceiveData(): Connection in state %d received packet %p with flags %X!\n", fState, buffer, header.flags));
|
status_t
|
||||||
switch (fState) {
|
TCPConnection::UpdateTimeWait()
|
||||||
case CLOSED:
|
{
|
||||||
case TIME_WAIT:
|
return B_OK;
|
||||||
gBufferModule->free(buffer);
|
}
|
||||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE)
|
|
||||||
return _Reset(byteAckd, 0);
|
|
||||||
|
|
||||||
return _Reset(0, byteRcvd + dataLength);
|
|
||||||
|
|
||||||
case LISTEN:
|
int32
|
||||||
// if packet is SYN, spawn new TCPConnection in SYN_RCVD state
|
TCPConnection::ListenReceive(tcp_segment_header& segment, net_buffer *buffer)
|
||||||
// and add it to the Connection Queue. The new TCPConnection
|
{
|
||||||
// must continue the handshake by replying with SYN+ACK. Any
|
// Essentially, we accept only TCP_FLAG_SYNCHRONIZE in this state,
|
||||||
// data in the packet must go into the new TCPConnection's receive
|
// but the error behaviour differs
|
||||||
// buffer.
|
if (segment.flags & TCP_FLAG_RESET)
|
||||||
// Otherwise, RST+ACK is sent.
|
return DROP;
|
||||||
// The current TCPConnection always remains in LISTEN state.
|
if (segment.flags & TCP_FLAG_ACKNOWLEDGE)
|
||||||
return B_ERROR;
|
return DROP | RESET;
|
||||||
|
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) == 0)
|
||||||
|
return DROP;
|
||||||
|
|
||||||
case SYNCHRONIZE_SENT:
|
// TODO: drop broadcast/multicast
|
||||||
if (header.flags & TCP_FLAG_RESET) {
|
|
||||||
|
// spawn new connection for accept()
|
||||||
|
net_socket *newSocket;
|
||||||
|
if (gSocketModule->spawn_pending_socket(socket, &newSocket) < B_OK)
|
||||||
|
return DROP;
|
||||||
|
|
||||||
|
gAddressModule->set_to((sockaddr *)&newSocket->address,
|
||||||
|
(sockaddr *)&buffer->destination);
|
||||||
|
gAddressModule->set_to((sockaddr *)&newSocket->peer,
|
||||||
|
(sockaddr *)&buffer->source);
|
||||||
|
|
||||||
|
TCPConnection *connection = (TCPConnection *)newSocket->first_protocol;
|
||||||
|
|
||||||
|
// TODO: proper error handling!
|
||||||
|
|
||||||
|
connection->fRoute = gDatalinkModule->get_route(gDomain,
|
||||||
|
(sockaddr *)&newSocket->address);
|
||||||
|
if (connection->fRoute == NULL)
|
||||||
|
return DROP;
|
||||||
|
|
||||||
|
if (insert_connection(connection) < B_OK)
|
||||||
|
return DROP;
|
||||||
|
|
||||||
|
connection->fState = SYNCHRONIZE_SENT;
|
||||||
|
|
||||||
|
benaphore_lock(&connection->fSendLock);
|
||||||
|
status_t status = connection->_SendQueuedData(
|
||||||
|
TCP_FLAG_SYNCHRONIZE | TCP_FLAG_ACKNOWLEDGE, false);
|
||||||
|
benaphore_unlock(&connection->fSendLock);
|
||||||
|
|
||||||
|
if (status < B_OK)
|
||||||
|
return DROP;
|
||||||
|
|
||||||
|
return connection->Receive(segment, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
TCPConnection::SynchronizeSentReceive(tcp_segment_header& segment, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
if (segment.flags & TCP_FLAG_RESET) {
|
||||||
fError = ECONNREFUSED;
|
fError = ECONNREFUSED;
|
||||||
fState = CLOSED;
|
fState = CLOSED;
|
||||||
return B_ERROR;
|
return DROP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE && !_IsAcknowledgeValid(byteAckd))
|
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
|
||||||
return _Reset(byteAckd, 0);
|
&& !_IsAcknowledgeValid(segment.acknowledge))
|
||||||
|
return DROP | RESET;
|
||||||
|
|
||||||
if (header.flags & TCP_FLAG_SYNCHRONIZE) {
|
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) == 0)
|
||||||
fNextByteToRead = fNextByteExpected = ntohl(header.sequence_num) + 1;
|
return DROP;
|
||||||
flags |= TCP_FLAG_ACKNOWLEDGE;
|
|
||||||
fLastByteAckd = byteAckd;
|
|
||||||
|
|
||||||
// cancel resend of this segment
|
fReceiveNext = segment.sequence + 1;
|
||||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE)
|
fLastAcknowledged = segment.acknowledge;
|
||||||
nextState = ESTABLISHED;
|
|
||||||
else {
|
|
||||||
nextState = SYNCHRONIZE_RECEIVED;
|
|
||||||
flags |= TCP_FLAG_SYNCHRONIZE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SYNCHRONIZE_RECEIVED:
|
if (segment.flags & TCP_FLAG_ACKNOWLEDGE) {
|
||||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE && _IsAcknowledgeValid(byteAckd))
|
// the connection has been established
|
||||||
fState = ESTABLISHED;
|
fState = ESTABLISHED;
|
||||||
else
|
} else {
|
||||||
_Reset(byteAckd, 0);
|
// simultaneous open
|
||||||
break;
|
fState = SYNCHRONIZE_RECEIVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Receive(segment, buffer) | IMMEDIATE_ACKNOWLEDGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
TCPConnection::Receive(tcp_segment_header& segment, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
TRACE(("TCP:%p.ReceiveData(): Connection in state %d received packet %p with flags 0x%x, seq %lu, ack %lu!\n",
|
||||||
|
this, fState, buffer, segment.flags, segment.sequence, segment.acknowledge));
|
||||||
|
|
||||||
|
// TODO: rethink locking!
|
||||||
|
|
||||||
default:
|
|
||||||
// In a synchronized state.
|
|
||||||
// first check that the received sequence number is good
|
// first check that the received sequence number is good
|
||||||
if (_IsSequenceValid(byteRcvd, dataLength)) {
|
if (_IsSequenceValid(segment.sequence, buffer->size)) {
|
||||||
// If a valid RST was received, terminate the connection.
|
// If a valid RST was received, terminate the connection.
|
||||||
if (header.flags & TCP_FLAG_RESET) {
|
if (segment.flags & TCP_FLAG_RESET) {
|
||||||
fError = ECONNREFUSED;
|
fError = ECONNREFUSED;
|
||||||
fState = CLOSED;
|
fState = CLOSED;
|
||||||
return B_ERROR;
|
return DROP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE && _IsAcknowledgeValid(byteAckd)) {
|
int32 action = KEEP;
|
||||||
fLastByteAckd = byteAckd;
|
status_t status;
|
||||||
if (fLastByteAckd == fNextByteToWrite) {
|
|
||||||
|
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
|
||||||
|
&& _IsAcknowledgeValid(segment.acknowledge)) {
|
||||||
|
fLastAcknowledged = segment.acknowledge;
|
||||||
|
if (fLastAcknowledged == fSendNext) {
|
||||||
if (fState == WAIT_FOR_FINISH_ACKNOWLEDGE) {
|
if (fState == WAIT_FOR_FINISH_ACKNOWLEDGE) {
|
||||||
nextState = CLOSED;
|
fState = CLOSED;
|
||||||
status = hash_remove(gConnectionHash, this);
|
status = remove_connection(this);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return DROP;
|
||||||
}
|
} else if (fState == CLOSING) {
|
||||||
if (fState == CLOSING) {
|
fState = TIME_WAIT;
|
||||||
nextState = TIME_WAIT;
|
status = remove_connection(this);
|
||||||
status = hash_remove(gConnectionHash, this);
|
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return DROP;
|
||||||
|
} else if (fState == FINISH_SENT)
|
||||||
|
fState = FINISH_ACKNOWLEDGED;
|
||||||
}
|
}
|
||||||
if (fState == FINISH_SENT) {
|
if (segment.flags & TCP_FLAG_FINISH) {
|
||||||
nextState = FINISH_ACKNOWLEDGED;
|
fReceiveNext++;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (header.flags & TCP_FLAG_FINISH) {
|
|
||||||
// other side is closing connection; change states
|
// other side is closing connection; change states
|
||||||
switch (fState) {
|
switch (fState) {
|
||||||
case ESTABLISHED:
|
case ESTABLISHED:
|
||||||
nextState = FINISH_RECEIVED;
|
fState = FINISH_RECEIVED;
|
||||||
fNextByteExpected++;
|
|
||||||
break;
|
break;
|
||||||
case FINISH_ACKNOWLEDGED:
|
case FINISH_ACKNOWLEDGED:
|
||||||
nextState = TIME_WAIT;
|
fState = TIME_WAIT;
|
||||||
fNextByteExpected++;
|
|
||||||
break;
|
break;
|
||||||
case FINISH_RECEIVED:
|
case FINISH_RECEIVED:
|
||||||
if (fLastByteAckd == fNextByteToWrite) {
|
if (fLastAcknowledged == fSendNext) {
|
||||||
// our FIN has been ACKd: go to TIME_WAIT
|
fState = TIME_WAIT;
|
||||||
nextState = TIME_WAIT;
|
status = remove_connection(this);
|
||||||
status = hash_remove(gConnectionHash, this);
|
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return DROP;
|
||||||
gStackModule->set_timer(&fTimer, TCP_MAX_SEGMENT_LIFETIME);
|
|
||||||
|
//gStackModule->set_timer(&fTimer, TCP_MAX_SEGMENT_LIFETIME);
|
||||||
} else
|
} else
|
||||||
nextState = CLOSING;
|
fState = CLOSING;
|
||||||
fNextByteExpected++;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flags |= TCP_FLAG_ACKNOWLEDGE;
|
if (fState != FINISH_ACKNOWLEDGED)
|
||||||
|
action |= ACKNOWLEDGE;
|
||||||
} 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 _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, true);
|
return DROP | ACKNOWLEDGE;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
TRACE(("TCP %p.Receive():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.
|
// state machine is done switching states and the data is good.
|
||||||
// put it in the receive buffer
|
// put it in the receive buffer
|
||||||
// TODO: This isn't the most efficient way to do it, and will need to be changed
|
// TODO: This isn't the most efficient way to do it, and will need to be changed
|
||||||
// to deal with Silly Window Syndrome
|
// to deal with Silly Window Syndrome
|
||||||
bufferHeader.Remove(headerLength);
|
|
||||||
if (buffer->size > 0) {
|
if (buffer->size > 0) {
|
||||||
status = _EnqueueReceivedData(buffer, byteRcvd);
|
status = _EnqueueReceivedData(buffer, segment.sequence);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return DROP;
|
||||||
} else
|
} else
|
||||||
gBufferModule->free(buffer);
|
gBufferModule->free(buffer);
|
||||||
|
|
||||||
if (fState != CLOSING && fState != WAIT_FOR_FINISH_ACKNOWLEDGE) {
|
if (fState != CLOSING && fState != WAIT_FOR_FINISH_ACKNOWLEDGE)
|
||||||
status = _SendQueuedData(flags, false);
|
action &= ACKNOWLEDGE;
|
||||||
if (status != B_OK)
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
fState = nextState;
|
return action;
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
TCPConnection::_Reset(uint32 sequence, uint32 acknowledge)
|
|
||||||
{
|
|
||||||
TRACE(("TCP:%p.Reset()\n", this));
|
|
||||||
net_buffer *reply = gBufferModule->create(512);
|
|
||||||
if (reply == NULL)
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
|
||||||
gAddressModule->set_to((sockaddr *)&reply->source, (sockaddr *)&socket->address);
|
|
||||||
gAddressModule->set_to((sockaddr *)&reply->destination, (sockaddr *)&socket->peer);
|
|
||||||
|
|
||||||
status_t status = add_tcp_header(reply,
|
|
||||||
TCP_FLAG_RESET | (acknowledge == 0 ? 0 : TCP_FLAG_ACKNOWLEDGE),
|
|
||||||
sequence, acknowledge, 0);
|
|
||||||
if (status != B_OK) {
|
|
||||||
gBufferModule->free(reply);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE(("TCP: Reset():Sending RST...\n"));
|
|
||||||
|
|
||||||
status = next->module->send_routed_data(next, fRoute, reply);
|
|
||||||
if (status != B_OK) {
|
|
||||||
// if sending failed, we stay responsible for the buffer
|
|
||||||
gBufferModule->free(reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -736,9 +736,10 @@ TCPConnection::ResendSegment(struct net_timer *timer, void *data)
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sends a TCP packet with the specified \a flags. If there is any data in
|
Sends a TCP packet with the specified \a flags. If there is any data in
|
||||||
the send buffer and \a empty is false, fEffectiveWindow bytes or less of it are sent as well.
|
the send buffer and \a empty is false, fEffectiveWindow bytes or less of
|
||||||
|
it are sent as well.
|
||||||
Sequence and Acknowledgement numbers are filled in accordingly.
|
Sequence and Acknowledgement numbers are filled in accordingly.
|
||||||
The fLock benaphore must be held before calling.
|
The send lock must be held before calling.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
TCPConnection::_SendQueuedData(uint16 flags, bool empty)
|
TCPConnection::_SendQueuedData(uint16 flags, bool empty)
|
||||||
@@ -750,49 +751,51 @@ TCPConnection::_SendQueuedData(uint16 flags, bool empty)
|
|||||||
|
|
||||||
net_buffer *buffer;
|
net_buffer *buffer;
|
||||||
uint32 effectiveWindow = min_c(next->module->get_mtu(next,
|
uint32 effectiveWindow = min_c(next->module->get_mtu(next,
|
||||||
(sockaddr *)&socket->address), fNextByteToWrite - fNextByteToSend);
|
(sockaddr *)&socket->address), fSendWindow);
|
||||||
|
|
||||||
if (empty || effectiveWindow == 0 || fSendBuffer == NULL || fSendBuffer->size == 0) {
|
if (empty || effectiveWindow == 0 || fSendBuffer == NULL || fSendBuffer->size == 0) {
|
||||||
|
if (flags == 0) {
|
||||||
|
// there is just nothing left to do
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
buffer = gBufferModule->create(256);
|
buffer = gBufferModule->create(256);
|
||||||
TRACE(("TCP: Sending Buffer %p\n", buffer));
|
|
||||||
if (buffer == NULL)
|
if (buffer == NULL)
|
||||||
return ENOBUFS;
|
return ENOBUFS;
|
||||||
} else {
|
} else {
|
||||||
|
if (effectiveWindow == fSendBuffer->size) {
|
||||||
buffer = fSendBuffer;
|
buffer = fSendBuffer;
|
||||||
if (effectiveWindow == fSendBuffer->size)
|
|
||||||
fSendBuffer = NULL;
|
fSendBuffer = NULL;
|
||||||
else
|
} else
|
||||||
fSendBuffer = gBufferModule->split(fSendBuffer, effectiveWindow);
|
buffer = gBufferModule->split(fSendBuffer, effectiveWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
gAddressModule->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address);
|
gAddressModule->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address);
|
||||||
gAddressModule->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer);
|
gAddressModule->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer);
|
||||||
TRACE(("TCP:%p.SendQueuedData() to address %s\n", this,
|
TRACE(("TCP:%p.SendQueuedData() buffer %p, from address %s to %s\n", this,
|
||||||
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data()));
|
buffer,
|
||||||
TRACE(("TCP:%p.SendQueuedData() from address %s\n", this,
|
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data(),
|
||||||
AddressString(gDomain, (sockaddr *)&buffer->source, true).Data()));
|
AddressString(gDomain, (sockaddr *)&buffer->source, true).Data()));
|
||||||
|
|
||||||
uint16 advertisedWindow = TCP_MAX_RECV_BUF - (fNextByteExpected - fNextByteToRead);
|
|
||||||
uint32 size = buffer->size;
|
uint32 size = buffer->size;
|
||||||
|
|
||||||
status_t status = add_tcp_header(buffer, flags, fNextByteToSend,
|
status_t status = add_tcp_header(buffer, flags, fSendNext,
|
||||||
fNextByteExpected, advertisedWindow);
|
fReceiveNext, fReceiveWindow);
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
gBufferModule->free(buffer);
|
gBufferModule->free(buffer);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only count 1 SYN, the 1 sent when transitioning from CLOSED or LISTEN
|
// Only count 1 SYN, the 1 sent when transitioning from CLOSED or LISTEN
|
||||||
if ((flags & TCP_FLAG_SYNCHRONIZE) != 0 && (fState == CLOSED || fState == LISTEN))
|
if ((flags & TCP_FLAG_SYNCHRONIZE) != 0)
|
||||||
fNextByteToSend++;
|
fSendNext++;
|
||||||
|
|
||||||
// Only count 1 FIN, the 1 sent when transitioning from
|
// Only count 1 FIN, the 1 sent when transitioning from
|
||||||
// ESTABLISHED, SYNCHRONIZE_RECEIVED or FINISH_RECEIVED
|
// ESTABLISHED, SYNCHRONIZE_RECEIVED or FINISH_RECEIVED
|
||||||
if ((flags & TCP_FLAG_FINISH) != 0
|
if ((flags & TCP_FLAG_FINISH) != 0)
|
||||||
&& (fState == SYNCHRONIZE_RECEIVED || fState == ESTABLISHED || fState == FINISH_RECEIVED))
|
fSendNext++;
|
||||||
fNextByteToSend++;
|
|
||||||
|
|
||||||
fNextByteToSend += size;
|
fSendNext += size;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
tcp_segment *segment = new(std::nothrow)
|
tcp_segment *segment = new(std::nothrow)
|
||||||
@@ -830,11 +833,10 @@ TCPConnection::Hash(void *_connection, const void *_key, uint32 range)
|
|||||||
{
|
{
|
||||||
if (_connection != NULL) {
|
if (_connection != NULL) {
|
||||||
TCPConnection *connection = (TCPConnection *)_connection;
|
TCPConnection *connection = (TCPConnection *)_connection;
|
||||||
return gAddressModule->hash_address_pair(
|
return gAddressModule->hash_address_pair((sockaddr *)&connection->socket->address,
|
||||||
(sockaddr *)&connection->socket->address, (sockaddr *)&connection->socket->peer) % range;
|
(sockaddr *)&connection->socket->peer) % range;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tcp_connection_key *key = (tcp_connection_key *)_key;
|
const tcp_connection_key *key = (tcp_connection_key *)_key;
|
||||||
return gAddressModule->hash_address_pair(
|
return gAddressModule->hash_address_pair(key->local, key->peer) % range;
|
||||||
key->local, key->peer) % range;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class TCPConnection : public net_protocol {
|
|||||||
TCPConnection(net_socket *socket);
|
TCPConnection(net_socket *socket);
|
||||||
~TCPConnection();
|
~TCPConnection();
|
||||||
|
|
||||||
|
status_t InitCheck() const;
|
||||||
|
|
||||||
status_t Open();
|
status_t Open();
|
||||||
status_t Close();
|
status_t Close();
|
||||||
status_t Free();
|
status_t Free();
|
||||||
@@ -33,17 +35,24 @@ class TCPConnection : public net_protocol {
|
|||||||
status_t Listen(int count);
|
status_t Listen(int count);
|
||||||
status_t Shutdown(int direction);
|
status_t Shutdown(int direction);
|
||||||
status_t SendData(net_buffer *buffer);
|
status_t SendData(net_buffer *buffer);
|
||||||
status_t SendRoutedData(net_route *route, net_buffer *buffer);
|
|
||||||
size_t SendAvailable();
|
size_t SendAvailable();
|
||||||
status_t ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer);
|
status_t ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer);
|
||||||
size_t ReadAvailable();
|
size_t ReadAvailable();
|
||||||
|
|
||||||
status_t ReceiveData(net_buffer *buffer);
|
tcp_state State() const { return fState; }
|
||||||
|
|
||||||
|
status_t DelayedAcknowledge();
|
||||||
|
status_t SendAcknowledge();
|
||||||
|
status_t UpdateTimeWait();
|
||||||
|
int32 ListenReceive(tcp_segment_header& segment, net_buffer *buffer);
|
||||||
|
int32 SynchronizeSentReceive(tcp_segment_header& segment,
|
||||||
|
net_buffer *buffer);
|
||||||
|
int32 Receive(tcp_segment_header& segment, net_buffer *buffer);
|
||||||
|
|
||||||
static void ResendSegment(struct net_timer *timer, void *data);
|
static void ResendSegment(struct net_timer *timer, void *data);
|
||||||
static int Compare(void *_packet, const void *_key);
|
static int Compare(void *_packet, const void *_key);
|
||||||
static uint32 Hash(void *_packet, const void *_key, uint32 range);
|
static uint32 Hash(void *_packet, const void *_key, uint32 range);
|
||||||
static int32 HashOffset() { return offsetof(TCPConnection, fHashLink); }
|
static int32 HashOffset() { return offsetof(TCPConnection, fHashNext); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _IsAcknowledgeValid(uint32 acknowledge);
|
bool _IsAcknowledgeValid(uint32 acknowledge);
|
||||||
@@ -51,34 +60,38 @@ class TCPConnection : public net_protocol {
|
|||||||
|
|
||||||
status_t _SendQueuedData(uint16 flags, bool empty);
|
status_t _SendQueuedData(uint16 flags, bool empty);
|
||||||
status_t _EnqueueReceivedData(net_buffer *buffer, uint32 sequenceNumber);
|
status_t _EnqueueReceivedData(net_buffer *buffer, uint32 sequenceNumber);
|
||||||
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);
|
||||||
|
|
||||||
uint32 fLastByteAckd;
|
TCPConnection *fHashNext;
|
||||||
uint32 fNextByteToSend;
|
|
||||||
uint32 fNextByteToWrite;
|
|
||||||
|
|
||||||
uint32 fNextByteToRead;
|
benaphore fSendLock;
|
||||||
uint32 fNextByteExpected;
|
benaphore fReceiveLock;
|
||||||
uint32 fLastByteReceived;
|
benaphore fLock;
|
||||||
|
sem_id fAcceptSemaphore;
|
||||||
bigtime_t fAvgRTT;
|
|
||||||
|
|
||||||
|
uint32 fLastAcknowledged;
|
||||||
|
uint32 fSendNext;
|
||||||
|
uint32 fSendWindow;
|
||||||
net_buffer *fSendBuffer;
|
net_buffer *fSendBuffer;
|
||||||
|
|
||||||
|
net_route *fRoute;
|
||||||
|
// TODO: don't use a net_route, but a net_route_info!!!
|
||||||
|
|
||||||
|
uint32 fReceiveNext;
|
||||||
|
uint32 fReceiveWindow;
|
||||||
|
bigtime_t fAvgRTT;
|
||||||
net_buffer *fReceiveBuffer;
|
net_buffer *fReceiveBuffer;
|
||||||
|
|
||||||
|
tcp_state fState;
|
||||||
|
status_t fError;
|
||||||
|
vint32 fDelayedAcknowledge;
|
||||||
|
|
||||||
struct list fReorderQueue;
|
struct list fReorderQueue;
|
||||||
struct list fWaitQueue;
|
struct list fWaitQueue;
|
||||||
|
|
||||||
TCPConnection *fHashLink;
|
// timer
|
||||||
tcp_state fState;
|
|
||||||
status_t fError;
|
|
||||||
benaphore fLock;
|
|
||||||
net_timer fTimer;
|
net_timer fTimer;
|
||||||
|
|
||||||
net_route *fRoute;
|
|
||||||
// TODO: don't use a net_route, but a net_route_info!!!
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TCP_CONNECTION_H
|
#endif // TCP_CONNECTION_H
|
||||||
|
|||||||
@@ -44,9 +44,10 @@ net_domain *gDomain;
|
|||||||
net_address_module_info *gAddressModule;
|
net_address_module_info *gAddressModule;
|
||||||
net_buffer_module_info *gBufferModule;
|
net_buffer_module_info *gBufferModule;
|
||||||
net_datalink_module_info *gDatalinkModule;
|
net_datalink_module_info *gDatalinkModule;
|
||||||
|
net_socket_module_info *gSocketModule;
|
||||||
net_stack_module_info *gStackModule;
|
net_stack_module_info *gStackModule;
|
||||||
hash_table *gConnectionHash;
|
hash_table *gConnectionHash;
|
||||||
benaphore gConnectionLock;
|
recursive_lock gConnectionLock;
|
||||||
|
|
||||||
|
|
||||||
#ifdef TRACE_TCP
|
#ifdef TRACE_TCP
|
||||||
@@ -55,7 +56,7 @@ benaphore gConnectionLock;
|
|||||||
void
|
void
|
||||||
tcp_dump_hash()
|
tcp_dump_hash()
|
||||||
{
|
{
|
||||||
BenaphoreLocker lock(&gConnectionLock);
|
RecursiveLocker lock(&gConnectionLock);
|
||||||
if (gDomain == NULL) {
|
if (gDomain == NULL) {
|
||||||
TRACE(("Unable to dump TCP Connections!\n"));
|
TRACE(("Unable to dump TCP Connections!\n"));
|
||||||
return;
|
return;
|
||||||
@@ -66,7 +67,7 @@ tcp_dump_hash()
|
|||||||
hash_rewind(gConnectionHash, &iterator);
|
hash_rewind(gConnectionHash, &iterator);
|
||||||
TRACE(("Active TCP Connections:\n"));
|
TRACE(("Active TCP Connections:\n"));
|
||||||
while ((connection = (TCPConnection *)hash_next(gConnectionHash, &iterator)) != NULL) {
|
while ((connection = (TCPConnection *)hash_next(gConnectionHash, &iterator)) != NULL) {
|
||||||
TRACE((" TCPConnection %p: %s, %s\n", connection,
|
TRACE((" TCPConnection %p: local %s, peer %s\n", connection,
|
||||||
AddressString(gDomain, (sockaddr *)&connection->socket->address, true).Data(),
|
AddressString(gDomain, (sockaddr *)&connection->socket->address, true).Data(),
|
||||||
AddressString(gDomain, (sockaddr *)&connection->socket->peer, true).Data()));
|
AddressString(gDomain, (sockaddr *)&connection->socket->peer, true).Data()));
|
||||||
}
|
}
|
||||||
@@ -104,8 +105,8 @@ set_domain(net_interface *interface = NULL)
|
|||||||
for \a flags, \a seq \a ack and \a advertisedWindow.
|
for \a flags, \a seq \a ack and \a advertisedWindow.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence, uint32 ack,
|
add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence,
|
||||||
uint16 advertisedWindow)
|
uint32 acknowledge, uint16 advertisedWindow)
|
||||||
{
|
{
|
||||||
buffer->protocol = IPPROTO_TCP;
|
buffer->protocol = IPPROTO_TCP;
|
||||||
|
|
||||||
@@ -117,16 +118,19 @@ add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence, uint32 ack,
|
|||||||
|
|
||||||
header.source_port = gAddressModule->get_port((sockaddr *)&buffer->source);
|
header.source_port = gAddressModule->get_port((sockaddr *)&buffer->source);
|
||||||
header.destination_port = gAddressModule->get_port((sockaddr *)&buffer->destination);
|
header.destination_port = gAddressModule->get_port((sockaddr *)&buffer->destination);
|
||||||
header.sequence_num = htonl(sequence);
|
header.sequence = htonl(sequence);
|
||||||
header.acknowledge_num = htonl(ack);
|
header.acknowledge = (flags & TCP_FLAG_ACKNOWLEDGE) ? htonl(acknowledge) : 0;
|
||||||
header.reserved = 0;
|
header.reserved = 0;
|
||||||
header.header_length = 5;
|
header.header_length = sizeof(tcp_header) >> 2;
|
||||||
// currently no options supported
|
// currently no options supported
|
||||||
header.flags = (uint8)flags;
|
header.flags = (uint8)flags;
|
||||||
header.advertised_window = htons(advertisedWindow);
|
header.advertised_window = htons(advertisedWindow);
|
||||||
header.checksum = 0;
|
header.checksum = 0;
|
||||||
header.urgent_ptr = 0;
|
header.urgent_offset = 0;
|
||||||
// urgent pointer not supported
|
// TODO: urgent pointer not yet supported
|
||||||
|
|
||||||
|
TRACE(("add_tcp_header(): buffer %p, flags 0x%x, seq %lu, ack %lu\n", buffer,
|
||||||
|
flags, sequence, acknowledge));
|
||||||
|
|
||||||
// compute and store checksum
|
// compute and store checksum
|
||||||
Checksum checksum;
|
Checksum checksum;
|
||||||
@@ -137,20 +141,134 @@ add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence, uint32 ack,
|
|||||||
<< (uint16)htons(buffer->size)
|
<< (uint16)htons(buffer->size)
|
||||||
<< Checksum::BufferHelper(buffer, gBufferModule);
|
<< Checksum::BufferHelper(buffer, gBufferModule);
|
||||||
header.checksum = checksum;
|
header.checksum = checksum;
|
||||||
TRACE(("TCP: Checksum for segment %p is %X\n", buffer, header.checksum));
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
reply_with_reset(tcp_segment_header& segment, net_buffer *buffer)
|
||||||
|
{
|
||||||
|
TRACE(("TCP: Sending RST...\n"));
|
||||||
|
|
||||||
|
net_buffer *reply = gBufferModule->create(512);
|
||||||
|
if (reply == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
gAddressModule->set_to((sockaddr *)&reply->source,
|
||||||
|
(sockaddr *)&buffer->destination);
|
||||||
|
gAddressModule->set_to((sockaddr *)&reply->destination,
|
||||||
|
(sockaddr *)&buffer->source);
|
||||||
|
|
||||||
|
uint8 flags = TCP_FLAG_RESET;
|
||||||
|
uint32 acknowledge = 0;
|
||||||
|
uint32 sequence = 0;
|
||||||
|
|
||||||
|
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) == 0) {
|
||||||
|
flags |= TCP_FLAG_ACKNOWLEDGE;
|
||||||
|
acknowledge = segment.sequence + buffer->size;
|
||||||
|
} else
|
||||||
|
sequence = segment.acknowledge;
|
||||||
|
|
||||||
|
status_t status = add_tcp_header(reply, flags,
|
||||||
|
sequence, acknowledge, 0);
|
||||||
|
if (status == B_OK) {
|
||||||
|
status = gDomain->module->send_data(NULL, reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != B_OK)
|
||||||
|
gBufferModule->free(reply);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TCPConnection *
|
||||||
|
lookup_connection(sockaddr *local, sockaddr *peer)
|
||||||
|
{
|
||||||
|
tcp_connection_key key;
|
||||||
|
key.local = local;
|
||||||
|
key.peer = peer;
|
||||||
|
|
||||||
|
return (TCPConnection *)hash_lookup(gConnectionHash, &key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
remove_connection(TCPConnection *connection)
|
||||||
|
{
|
||||||
|
RecursiveLocker hashLock(&gConnectionLock);
|
||||||
|
return hash_remove(gConnectionHash, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
insert_connection(TCPConnection *connection)
|
||||||
|
{
|
||||||
|
RecursiveLocker hashLock(&gConnectionLock);
|
||||||
|
|
||||||
|
tcp_connection_key key;
|
||||||
|
key.local = (sockaddr *)&connection->socket->address;
|
||||||
|
key.peer = (sockaddr *)&connection->socket->peer;
|
||||||
|
|
||||||
|
if (hash_lookup(gConnectionHash, &key) != NULL)
|
||||||
|
return EADDRINUSE;
|
||||||
|
|
||||||
|
return hash_insert(gConnectionHash, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TCPConnection *
|
||||||
|
find_connection(sockaddr *local, sockaddr *peer)
|
||||||
|
{
|
||||||
|
TCPConnection *connection = lookup_connection(local, peer);
|
||||||
|
if (connection != NULL) {
|
||||||
|
TRACE(("TCP: Received packet corresponds to explicit connection %p\n", connection));
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no explicit connection exists, check for wildcard connections
|
||||||
|
|
||||||
|
sockaddr wildcard;
|
||||||
|
gAddressModule->set_to_empty_address(&wildcard);
|
||||||
|
|
||||||
|
connection = lookup_connection(local, &wildcard);
|
||||||
|
if (connection != NULL) {
|
||||||
|
TRACE(("TCP: Received packet corresponds to wildcard connection %p\n", connection));
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr localWildcard;
|
||||||
|
gAddressModule->set_to_empty_address(&localWildcard);
|
||||||
|
gAddressModule->set_port(&localWildcard, gAddressModule->get_port(local));
|
||||||
|
|
||||||
|
connection = lookup_connection(&localWildcard, &wildcard);
|
||||||
|
if (connection != NULL) {
|
||||||
|
TRACE(("TCP: Received packet corresponds to local wildcard connection %p\n", connection));
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no matching connection exists
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - protocol API
|
// #pragma mark - protocol API
|
||||||
|
|
||||||
|
|
||||||
net_protocol *
|
net_protocol *
|
||||||
tcp_init_protocol(net_socket *socket)
|
tcp_init_protocol(net_socket *socket)
|
||||||
{
|
{
|
||||||
DUMP_TCP_HASH;
|
|
||||||
socket->protocol = IPPROTO_TCP;
|
socket->protocol = IPPROTO_TCP;
|
||||||
TCPConnection *protocol = new (std::nothrow) TCPConnection(socket);
|
TCPConnection *protocol = new (std::nothrow) TCPConnection(socket);
|
||||||
|
if (protocol == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (protocol->InitCheck() != B_OK) {
|
||||||
|
delete protocol;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
TRACE(("Creating new TCPConnection: %p\n", protocol));
|
TRACE(("Creating new TCPConnection: %p\n", protocol));
|
||||||
return protocol;
|
return protocol;
|
||||||
}
|
}
|
||||||
@@ -259,7 +377,8 @@ status_t
|
|||||||
tcp_send_routed_data(net_protocol *protocol, struct net_route *route,
|
tcp_send_routed_data(net_protocol *protocol, struct net_route *route,
|
||||||
net_buffer *buffer)
|
net_buffer *buffer)
|
||||||
{
|
{
|
||||||
return ((TCPConnection *)protocol)->SendRoutedData(route, buffer);
|
// TCP never sends routed data
|
||||||
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -313,67 +432,89 @@ tcp_receive_data(net_buffer *buffer)
|
|||||||
|
|
||||||
tcp_header &header = bufferHeader.Data();
|
tcp_header &header = bufferHeader.Data();
|
||||||
|
|
||||||
tcp_connection_key key;
|
uint16 headerLength = header.HeaderLength();
|
||||||
key.peer = (struct sockaddr *)&buffer->source;
|
if (headerLength < sizeof(tcp_header))
|
||||||
key.local = (struct sockaddr *)&buffer->destination;
|
return B_BAD_DATA;
|
||||||
|
|
||||||
// TODO: check TCP Checksum
|
// compute checksum using a pseudo IP header
|
||||||
|
Checksum checksum;
|
||||||
|
gAddressModule->checksum_address(&checksum, (sockaddr *)&buffer->source);
|
||||||
|
gAddressModule->checksum_address(&checksum, (sockaddr *)&buffer->destination);
|
||||||
|
checksum << (uint16)htons(IPPROTO_TCP)
|
||||||
|
<< (uint16)htons(buffer->size)
|
||||||
|
<< Checksum::BufferHelper(buffer, gBufferModule);
|
||||||
|
|
||||||
|
if (checksum != 0)
|
||||||
|
return B_BAD_DATA;
|
||||||
|
|
||||||
gAddressModule->set_port((struct sockaddr *)&buffer->source, header.source_port);
|
gAddressModule->set_port((struct sockaddr *)&buffer->source, header.source_port);
|
||||||
gAddressModule->set_port((struct sockaddr *)&buffer->destination, header.destination_port);
|
gAddressModule->set_port((struct sockaddr *)&buffer->destination, header.destination_port);
|
||||||
|
|
||||||
|
TRACE((" Looking for: peer %s, local %s\n",
|
||||||
|
AddressString(gDomain, (sockaddr *)&buffer->source, true).Data(),
|
||||||
|
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data()));
|
||||||
|
|
||||||
DUMP_TCP_HASH;
|
DUMP_TCP_HASH;
|
||||||
|
|
||||||
BenaphoreLocker hashLock(&gConnectionLock);
|
// TODO: process options!
|
||||||
TCPConnection *connection = (TCPConnection *)hash_lookup(gConnectionHash, &key);
|
tcp_segment_header segment;
|
||||||
TRACE(("TCP: Received packet corresponds to connection %p\n", connection));
|
segment.sequence = header.Sequence();
|
||||||
|
segment.acknowledge = header.Acknowledge();
|
||||||
|
segment.advertised_window = header.AdvertisedWindow();
|
||||||
|
segment.urgent_offset = header.UrgentOffset();
|
||||||
|
segment.flags = header.flags;
|
||||||
|
|
||||||
|
bufferHeader.Remove(headerLength);
|
||||||
|
// we no longer need to keep the header around
|
||||||
|
|
||||||
|
RecursiveLocker hashLock(&gConnectionLock);
|
||||||
|
int32 segmentAction = DROP;
|
||||||
|
|
||||||
|
TCPConnection *connection = find_connection((struct sockaddr *)&buffer->destination,
|
||||||
|
(struct sockaddr *)&buffer->source);
|
||||||
if (connection != NULL) {
|
if (connection != NULL) {
|
||||||
return connection->ReceiveData(buffer);
|
switch (connection->State()) {
|
||||||
} else {
|
case CLOSED:
|
||||||
/* TODO:
|
case TIME_WAIT:
|
||||||
No explicit connection exists. Check for wildcard connections:
|
connection->UpdateTimeWait();
|
||||||
First check if any connections exist where local = IPADDR_ANY
|
break;
|
||||||
then check when local = peer = IPADDR_ANY.
|
|
||||||
port numbers always remain the same */
|
|
||||||
|
|
||||||
// If no connection exists (and RESET is not set) send RESET
|
case LISTEN:
|
||||||
if ((header.flags & TCP_FLAG_RESET) == 0) {
|
segmentAction = connection->ListenReceive(segment, buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYNCHRONIZE_SENT:
|
||||||
|
segmentAction = connection->SynchronizeSentReceive(segment, buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYNCHRONIZE_RECEIVED:
|
||||||
|
case ESTABLISHED:
|
||||||
|
case FINISH_RECEIVED:
|
||||||
|
case WAIT_FOR_FINISH_ACKNOWLEDGE:
|
||||||
|
case FINISH_SENT:
|
||||||
|
case FINISH_ACKNOWLEDGED:
|
||||||
|
case CLOSING:
|
||||||
|
segmentAction = connection->Receive(segment, buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process acknowledge action as asked for by the *Receive() method
|
||||||
|
if (segmentAction & IMMEDIATE_ACKNOWLEDGE)
|
||||||
|
connection->SendAcknowledge();
|
||||||
|
else if (segmentAction & ACKNOWLEDGE)
|
||||||
|
connection->DelayedAcknowledge();
|
||||||
|
} else if ((segment.flags & TCP_FLAG_RESET) == 0)
|
||||||
|
segmentAction = DROP | RESET;
|
||||||
|
|
||||||
|
if (segmentAction & RESET) {
|
||||||
|
// send reset
|
||||||
TRACE(("TCP: Connection does not exist!\n"));
|
TRACE(("TCP: Connection does not exist!\n"));
|
||||||
net_buffer *reply = gBufferModule->create(512);
|
|
||||||
if (reply == NULL)
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
|
||||||
gAddressModule->set_to((sockaddr *)&reply->source,
|
reply_with_reset(segment, buffer);
|
||||||
(sockaddr *)&buffer->destination);
|
}
|
||||||
gAddressModule->set_to((sockaddr *)&reply->destination,
|
if (segmentAction & DROP)
|
||||||
(sockaddr *)&buffer->source);
|
gBufferModule->free(buffer);
|
||||||
|
|
||||||
uint32 sequence, acknowledge;
|
|
||||||
uint16 flags;
|
|
||||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE) {
|
|
||||||
sequence = ntohl(header.acknowledge_num);
|
|
||||||
acknowledge = 0;
|
|
||||||
flags = TCP_FLAG_RESET;
|
|
||||||
} else {
|
|
||||||
sequence = 0;
|
|
||||||
acknowledge = ntohl(header.sequence_num) + 1
|
|
||||||
+ buffer->size - ((uint32)header.header_length << 2);
|
|
||||||
flags = TCP_FLAG_RESET | TCP_FLAG_ACKNOWLEDGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t status = add_tcp_header(reply, flags, sequence, acknowledge, 0);
|
|
||||||
|
|
||||||
if (status == B_OK) {
|
|
||||||
TRACE(("TCP: Sending RST...\n"));
|
|
||||||
status = gDomain->module->send_data(NULL, reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status != B_OK) {
|
|
||||||
gBufferModule->free(reply);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,46 +551,51 @@ tcp_init()
|
|||||||
status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule);
|
status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
goto err1;
|
goto err1;
|
||||||
status = get_module(NET_DATALINK_MODULE_NAME, (module_info **)&gDatalinkModule);
|
status = get_module(NET_SOCKET_MODULE_NAME, (module_info **)&gSocketModule);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
goto err2;
|
goto err2;
|
||||||
|
status = get_module(NET_DATALINK_MODULE_NAME, (module_info **)&gDatalinkModule);
|
||||||
|
if (status < B_OK)
|
||||||
|
goto err3;
|
||||||
|
|
||||||
gConnectionHash = hash_init(MAX_HASH_TCP, TCPConnection::HashOffset(),
|
gConnectionHash = hash_init(MAX_HASH_TCP, TCPConnection::HashOffset(),
|
||||||
&TCPConnection::Compare, &TCPConnection::Hash);
|
&TCPConnection::Compare, &TCPConnection::Hash);
|
||||||
if (gConnectionHash == NULL)
|
if (gConnectionHash == NULL)
|
||||||
goto err3;
|
|
||||||
|
|
||||||
status = benaphore_init(&gConnectionLock, "TCP Hash Lock");
|
|
||||||
if (status < B_OK)
|
|
||||||
goto err4;
|
goto err4;
|
||||||
|
|
||||||
|
status = recursive_lock_init(&gConnectionLock, "tcp connection hash");
|
||||||
|
if (status < B_OK)
|
||||||
|
goto err5;
|
||||||
|
|
||||||
status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_IP,
|
status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_IP,
|
||||||
"network/protocols/tcp/v1",
|
"network/protocols/tcp/v1",
|
||||||
"network/protocols/ipv4/v1",
|
"network/protocols/ipv4/v1",
|
||||||
NULL);
|
NULL);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
goto err5;
|
goto err6;
|
||||||
|
|
||||||
status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||||||
"network/protocols/tcp/v1",
|
"network/protocols/tcp/v1",
|
||||||
"network/protocols/ipv4/v1",
|
"network/protocols/ipv4/v1",
|
||||||
NULL);
|
NULL);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
goto err5;
|
goto err6;
|
||||||
|
|
||||||
status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP,
|
status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP,
|
||||||
"network/protocols/tcp/v1");
|
"network/protocols/tcp/v1");
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
goto err5;
|
goto err6;
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
|
err6:
|
||||||
|
recursive_lock_destroy(&gConnectionLock);
|
||||||
err5:
|
err5:
|
||||||
benaphore_destroy(&gConnectionLock);
|
|
||||||
err4:
|
|
||||||
hash_uninit(gConnectionHash);
|
hash_uninit(gConnectionHash);
|
||||||
err3:
|
err4:
|
||||||
put_module(NET_DATALINK_MODULE_NAME);
|
put_module(NET_DATALINK_MODULE_NAME);
|
||||||
|
err3:
|
||||||
|
put_module(NET_SOCKET_MODULE_NAME);
|
||||||
err2:
|
err2:
|
||||||
put_module(NET_BUFFER_MODULE_NAME);
|
put_module(NET_BUFFER_MODULE_NAME);
|
||||||
err1:
|
err1:
|
||||||
@@ -463,9 +609,10 @@ err1:
|
|||||||
static status_t
|
static status_t
|
||||||
tcp_uninit()
|
tcp_uninit()
|
||||||
{
|
{
|
||||||
benaphore_destroy(&gConnectionLock);
|
recursive_lock_destroy(&gConnectionLock);
|
||||||
hash_uninit(gConnectionHash);
|
hash_uninit(gConnectionHash);
|
||||||
put_module(NET_DATALINK_MODULE_NAME);
|
put_module(NET_DATALINK_MODULE_NAME);
|
||||||
|
put_module(NET_SOCKET_MODULE_NAME);
|
||||||
put_module(NET_BUFFER_MODULE_NAME);
|
put_module(NET_BUFFER_MODULE_NAME);
|
||||||
put_module(NET_STACK_MODULE_NAME);
|
put_module(NET_STACK_MODULE_NAME);
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,18 @@
|
|||||||
|
|
||||||
#include <net_buffer.h>
|
#include <net_buffer.h>
|
||||||
#include <net_datalink.h>
|
#include <net_datalink.h>
|
||||||
|
#include <net_socket.h>
|
||||||
#include <net_stack.h>
|
#include <net_stack.h>
|
||||||
|
|
||||||
#include <util/khash.h>
|
#include <util/khash.h>
|
||||||
|
|
||||||
|
#include <ByteOrder.h>
|
||||||
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
|
||||||
|
class TCPConnection;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
// establishing a connection
|
// establishing a connection
|
||||||
CLOSED,
|
CLOSED,
|
||||||
@@ -40,8 +45,8 @@ typedef enum {
|
|||||||
struct tcp_header {
|
struct tcp_header {
|
||||||
uint16 source_port;
|
uint16 source_port;
|
||||||
uint16 destination_port;
|
uint16 destination_port;
|
||||||
uint32 sequence_num;
|
uint32 sequence;
|
||||||
uint32 acknowledge_num;
|
uint32 acknowledge;
|
||||||
struct {
|
struct {
|
||||||
#if B_HOST_IS_LENDIAN == 1
|
#if B_HOST_IS_LENDIAN == 1
|
||||||
uint8 reserved : 4;
|
uint8 reserved : 4;
|
||||||
@@ -54,7 +59,14 @@ struct tcp_header {
|
|||||||
uint8 flags;
|
uint8 flags;
|
||||||
uint16 advertised_window;
|
uint16 advertised_window;
|
||||||
uint16 checksum;
|
uint16 checksum;
|
||||||
uint16 urgent_ptr;
|
uint16 urgent_offset;
|
||||||
|
|
||||||
|
uint32 HeaderLength() const { return (uint32)header_length << 2; }
|
||||||
|
uint32 Sequence() const { return ntohl(sequence); }
|
||||||
|
uint32 Acknowledge() const { return ntohl(acknowledge); }
|
||||||
|
uint16 AdvertisedWindow() const { return ntohs(advertised_window); }
|
||||||
|
uint16 Checksum() const { return ntohs(checksum); }
|
||||||
|
uint16 UrgentOffset() const { return ntohs(urgent_offset); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// TCP flag constants
|
// TCP flag constants
|
||||||
@@ -67,6 +79,22 @@ struct tcp_header {
|
|||||||
#define TCP_FLAG_ECN 0x40 // Explicit Congestion Notification echo
|
#define TCP_FLAG_ECN 0x40 // Explicit Congestion Notification echo
|
||||||
#define TCP_FLAG_CWR 0x80 // Congestion Window Reduced
|
#define TCP_FLAG_CWR 0x80 // Congestion Window Reduced
|
||||||
|
|
||||||
|
struct tcp_segment_header {
|
||||||
|
uint32 sequence;
|
||||||
|
uint32 acknowledge;
|
||||||
|
uint16 advertised_window;
|
||||||
|
uint16 urgent_offset;
|
||||||
|
uint8 flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum tcp_segment_action {
|
||||||
|
KEEP = 0x00,
|
||||||
|
DROP = 0x01,
|
||||||
|
RESET = 0x02,
|
||||||
|
ACKNOWLEDGE = 0x04,
|
||||||
|
IMMEDIATE_ACKNOWLEDGE = 0x08,
|
||||||
|
};
|
||||||
|
|
||||||
struct tcp_connection_key {
|
struct tcp_connection_key {
|
||||||
const sockaddr *local;
|
const sockaddr *local;
|
||||||
const sockaddr *peer;
|
const sockaddr *peer;
|
||||||
@@ -77,12 +105,15 @@ extern net_domain *gDomain;
|
|||||||
extern net_address_module_info *gAddressModule;
|
extern net_address_module_info *gAddressModule;
|
||||||
extern net_buffer_module_info *gBufferModule;
|
extern net_buffer_module_info *gBufferModule;
|
||||||
extern net_datalink_module_info *gDatalinkModule;
|
extern net_datalink_module_info *gDatalinkModule;
|
||||||
|
extern net_socket_module_info *gSocketModule;
|
||||||
extern net_stack_module_info *gStackModule;
|
extern net_stack_module_info *gStackModule;
|
||||||
extern hash_table *gConnectionHash;
|
//extern hash_table *gConnectionHash;
|
||||||
extern benaphore gConnectionLock;
|
//extern benaphore gConnectionLock;
|
||||||
|
|
||||||
|
|
||||||
status_t add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence,
|
status_t add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence,
|
||||||
uint32 ack, uint16 advertisedWindow);
|
uint32 ack, uint16 advertisedWindow);
|
||||||
|
status_t remove_connection(TCPConnection *connection);
|
||||||
|
status_t insert_connection(TCPConnection *connection);
|
||||||
|
|
||||||
#endif // TCP_H
|
#endif // TCP_H
|
||||||
|
|||||||
Reference in New Issue
Block a user