* 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:
parent
fae6fb6d69
commit
30ea523ad1
|
@ -86,38 +86,59 @@ tcp_segment::~tcp_segment()
|
|||
|
||||
TCPConnection::TCPConnection(net_socket *socket)
|
||||
:
|
||||
fLastByteAckd(0), //system_time()),
|
||||
fNextByteToSend(fLastByteAckd),
|
||||
fNextByteToWrite(fLastByteAckd + 1),
|
||||
fNextByteToRead(0),
|
||||
fNextByteExpected(0),
|
||||
fLastByteReceived(0),
|
||||
fAvgRTT(TCP_INITIAL_RTT),
|
||||
fLastAcknowledged(0), //system_time()),
|
||||
fSendNext(fLastAcknowledged),
|
||||
fSendWindow(0),
|
||||
fSendBuffer(NULL),
|
||||
fRoute(NULL),
|
||||
fReceiveNext(0),
|
||||
fReceiveWindow(0),
|
||||
fAvgRTT(TCP_INITIAL_RTT),
|
||||
fReceiveBuffer(NULL),
|
||||
fState(CLOSED),
|
||||
fError(B_OK),
|
||||
fRoute(NULL)
|
||||
fError(B_OK)
|
||||
{
|
||||
benaphore_init(&fLock, "TCPConnection");
|
||||
gStackModule->init_timer(&fTimer, _TimeWait, this);
|
||||
list_init(&fReorderQueue);
|
||||
list_init(&fWaitQueue);
|
||||
|
||||
benaphore_init(&fReceiveLock, "tcp receive");
|
||||
benaphore_init(&fSendLock, "tcp send");
|
||||
fAcceptSemaphore = create_sem(0, "tcp accept");
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
TCPConnection::_IsAcknowledgeValid(uint32 acknowledge)
|
||||
{
|
||||
return fLastByteAckd > fNextByteToSend
|
||||
? acknowledge >= fLastByteAckd || acknowledge <= fNextByteToSend
|
||||
: acknowledge >= fLastByteAckd && acknowledge <= fNextByteToSend;
|
||||
dprintf("last byte ackd %lu, next byte to send %lu, ack %lu\n", fLastAcknowledged, fSendNext, acknowledge);
|
||||
return fLastAcknowledged > fSendNext
|
||||
? acknowledge >= fLastAcknowledged || acknowledge <= fSendNext
|
||||
: acknowledge >= fLastAcknowledged && acknowledge <= fSendNext;
|
||||
}
|
||||
|
||||
|
||||
|
@ -126,16 +147,16 @@ TCPConnection::_IsSequenceValid(uint32 sequence, uint32 length)
|
|||
{
|
||||
uint32 end = sequence + length;
|
||||
|
||||
if (fNextByteToRead < fNextByteToRead + TCP_MAX_RECV_BUF) {
|
||||
return sequence >= fNextByteToRead
|
||||
&& end <= fNextByteToRead + TCP_MAX_RECV_BUF;
|
||||
if (fReceiveNext < fReceiveNext + TCP_MAX_RECV_BUF) {
|
||||
return sequence >= fReceiveNext
|
||||
&& end <= fReceiveNext + TCP_MAX_RECV_BUF;
|
||||
}
|
||||
|
||||
// integer overflow
|
||||
return (sequence >= fNextByteToRead
|
||||
|| sequence <= fNextByteToRead + TCP_MAX_RECV_BUF)
|
||||
&& (end >= fNextByteToRead
|
||||
|| end <= fNextByteToRead + TCP_MAX_RECV_BUF);
|
||||
return (sequence >= fReceiveNext
|
||||
|| sequence <= fReceiveNext + TCP_MAX_RECV_BUF)
|
||||
&& (end >= fReceiveNext
|
||||
|| end <= fReceiveNext + TCP_MAX_RECV_BUF);
|
||||
}
|
||||
|
||||
|
||||
|
@ -143,15 +164,7 @@ status_t
|
|||
TCPConnection::Open()
|
||||
{
|
||||
TRACE(("%p.Open()\n", this));
|
||||
if (gAddressModule == NULL)
|
||||
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);
|
||||
// nothing to do here...
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
@ -159,23 +172,28 @@ TCPConnection::Open()
|
|||
status_t
|
||||
TCPConnection::Close()
|
||||
{
|
||||
BenaphoreLocker lock(&fLock);
|
||||
BenaphoreLocker lock(&fSendLock);
|
||||
TRACE(("TCP:%p.Close()\n", this));
|
||||
if (fState == SYNCHRONIZE_SENT || fState == LISTEN) {
|
||||
fState = CLOSED;
|
||||
return B_OK;
|
||||
}
|
||||
tcp_state nextState = CLOSED;
|
||||
|
||||
tcp_state previousState = fState;
|
||||
|
||||
if (fState == SYNCHRONIZE_RECEIVED || fState == ESTABLISHED)
|
||||
nextState = FINISH_SENT;
|
||||
if (fState == FINISH_RECEIVED)
|
||||
nextState = WAIT_FOR_FINISH_ACKNOWLEDGE;
|
||||
fState = FINISH_SENT;
|
||||
else if (fState == FINISH_RECEIVED)
|
||||
fState = WAIT_FOR_FINISH_ACKNOWLEDGE;
|
||||
else
|
||||
fState = CLOSED;
|
||||
|
||||
status_t status = _SendQueuedData(TCP_FLAG_FINISH | TCP_FLAG_ACKNOWLEDGE, false);
|
||||
if (status != B_OK)
|
||||
if (status != B_OK) {
|
||||
fState = previousState;
|
||||
return status;
|
||||
}
|
||||
|
||||
fState = nextState;
|
||||
TRACE(("TCP: %p.Close(): Entering state %d\n", this, fState));
|
||||
// TODO: do i need to wait until fState returns to CLOSED?
|
||||
return B_OK;
|
||||
|
@ -187,14 +205,8 @@ TCPConnection::Free()
|
|||
{
|
||||
TRACE(("TCP:%p.Free()\n", this));
|
||||
|
||||
BenaphoreLocker hashLock(&gConnectionLock);
|
||||
BenaphoreLocker lock(&fLock);
|
||||
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);
|
||||
}
|
||||
// TODO: if this connection is not in the hash, we don't have to call this one
|
||||
remove_connection(this);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
@ -212,70 +224,55 @@ TCPConnection::Connect(const struct sockaddr *address)
|
|||
if (address->sa_family != AF_INET)
|
||||
return EAFNOSUPPORT;
|
||||
|
||||
benaphore_lock(&gConnectionLock);
|
||||
// want to release lock later, so no autolock
|
||||
BenaphoreLocker lock(&fLock);
|
||||
BenaphoreLocker lock(&fSendLock);
|
||||
|
||||
// Can only call Connect from CLOSED or LISTEN states
|
||||
// Can only call connect() from CLOSED or LISTEN states
|
||||
// otherwise connection is considered already connected
|
||||
if (fState != CLOSED && fState != LISTEN) {
|
||||
benaphore_unlock(&gConnectionLock);
|
||||
if (fState == LISTEN) {
|
||||
// this socket is about to connect; remove pending connections in the backlog
|
||||
gSocketModule->set_max_backlog(socket, 0);
|
||||
} else if (fState != CLOSED)
|
||||
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
|
||||
// TODO: get a net_route_info instead!
|
||||
if (fRoute == NULL) {
|
||||
fRoute = gDatalinkModule->get_route(gDomain, (sockaddr *)address);
|
||||
TRACE(("TCP: Connect(): Using Route %p\n", fRoute));
|
||||
if (fRoute == NULL) {
|
||||
benaphore_unlock(&gConnectionLock);
|
||||
TRACE((" TCP: Connect(): Using Route %p\n", fRoute));
|
||||
if (fRoute == NULL)
|
||||
return ENETUNREACH;
|
||||
}
|
||||
}
|
||||
|
||||
// need to associate this connection with a real address, not INADDR_ANY
|
||||
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);
|
||||
// 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
|
||||
tcp_connection_key key;
|
||||
key.local = (sockaddr *)&socket->address;
|
||||
key.peer = address;
|
||||
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);
|
||||
status_t status = insert_connection(this);
|
||||
if (status < B_OK) {
|
||||
TRACE((" TCP: Connect(): could not add connection: %s!\n", strerror(status)));
|
||||
return status;
|
||||
}
|
||||
|
||||
// done manipulating the hash, release the lock
|
||||
benaphore_unlock(&gConnectionLock);
|
||||
|
||||
TRACE(("TCP: Connect(): starting 3-way handshake...\n"));
|
||||
// send SYN
|
||||
status = _SendQueuedData(TCP_FLAG_SYNCHRONIZE, false);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
TRACE((" TCP: Connect(): starting 3-way handshake...\n"));
|
||||
|
||||
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
|
||||
TRACE(("TCP: Connect(): Connection complete\n"));
|
||||
TRACE((" TCP: Connect(): Connection complete\n"));
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
@ -284,7 +281,19 @@ status_t
|
|||
TCPConnection::Accept(struct net_socket **_acceptedSocket)
|
||||
{
|
||||
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)
|
||||
return EAFNOSUPPORT;
|
||||
|
||||
BenaphoreLocker hashLock(&gConnectionLock);
|
||||
BenaphoreLocker lock(&fLock);
|
||||
BenaphoreLocker lock(&fSendLock);
|
||||
// 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:
|
||||
status_t status = next->module->bind(next, address);
|
||||
|
@ -306,21 +318,18 @@ TCPConnection::Bind(sockaddr *address)
|
|||
return status;
|
||||
|
||||
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
|
||||
// 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 {
|
||||
// TODO: Check for Socket flags
|
||||
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;
|
||||
status = insert_connection(this);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
@ -329,10 +338,10 @@ TCPConnection::Unbind(struct sockaddr *address)
|
|||
{
|
||||
TRACE(("TCP:%p.Unbind()\n", this ));
|
||||
|
||||
BenaphoreLocker hashLock(&gConnectionLock);
|
||||
BenaphoreLocker lock(&fLock);
|
||||
BenaphoreLocker lock(&fSendLock);
|
||||
// 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)
|
||||
return status;
|
||||
|
||||
|
@ -346,9 +355,9 @@ status_t
|
|||
TCPConnection::Listen(int count)
|
||||
{
|
||||
TRACE(("TCP:%p.Listen()\n", this));
|
||||
BenaphoreLocker lock(&fLock);
|
||||
BenaphoreLocker lock(&fSendLock);
|
||||
if (fState != CLOSED)
|
||||
return B_ERROR;
|
||||
return B_BAD_VALUE;
|
||||
|
||||
fState = LISTEN;
|
||||
return B_OK;
|
||||
|
@ -359,6 +368,7 @@ status_t
|
|||
TCPConnection::Shutdown(int direction)
|
||||
{
|
||||
TRACE(("TCP:%p.Shutdown()\n", this));
|
||||
// TODO: implement shutdown!
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
@ -370,8 +380,8 @@ status_t
|
|||
TCPConnection::SendData(net_buffer *buffer)
|
||||
{
|
||||
TRACE(("TCP:%p.SendData()\n", this));
|
||||
size_t bufferSize = buffer->size;
|
||||
BenaphoreLocker lock(&fLock);
|
||||
|
||||
BenaphoreLocker lock(&fSendLock);
|
||||
if (fSendBuffer != NULL) {
|
||||
status_t status = gBufferModule->merge(fSendBuffer, buffer, true);
|
||||
if (status != B_OK)
|
||||
|
@ -379,28 +389,15 @@ TCPConnection::SendData(net_buffer *buffer)
|
|||
} else
|
||||
fSendBuffer = buffer;
|
||||
|
||||
fNextByteToWrite += bufferSize;
|
||||
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
|
||||
TCPConnection::SendAvailable()
|
||||
{
|
||||
TRACE(("TCP:%p.SendAvailable()\n", this));
|
||||
BenaphoreLocker lock(&fLock);
|
||||
BenaphoreLocker lock(&fSendLock);
|
||||
if (fSendBuffer != NULL)
|
||||
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));
|
||||
|
||||
BenaphoreLocker lock(&fLock);
|
||||
BenaphoreLocker lock(&fReceiveLock);
|
||||
|
||||
// must be in a synchronous state
|
||||
if (fState != ESTABLISHED || fState != FINISH_SENT || fState != FINISH_ACKNOWLEDGED) {
|
||||
|
@ -441,7 +438,7 @@ size_t
|
|||
TCPConnection::ReadAvailable()
|
||||
{
|
||||
TRACE(("TCP:%p.ReadAvailable()\n", this));
|
||||
BenaphoreLocker lock(&fLock);
|
||||
BenaphoreLocker lock(&fReceiveLock);
|
||||
if (fReceiveBuffer != NULL)
|
||||
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
|
||||
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));
|
||||
status_t status;
|
||||
|
||||
if (sequence == fNextByteExpected) {
|
||||
if (sequence == fReceiveNext) {
|
||||
// first check if the received buffer meets up with the first
|
||||
// segment in the ReorderQueue
|
||||
tcp_segment *next;
|
||||
|
@ -478,16 +475,16 @@ TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
|
|||
break;
|
||||
}
|
||||
|
||||
fNextByteExpected += buffer->size;
|
||||
if (fReceiveBuffer == NULL)
|
||||
fReceiveBuffer = buffer;
|
||||
else {
|
||||
fReceiveNext += buffer->size;
|
||||
|
||||
if (fReceiveBuffer != NULL) {
|
||||
status = gBufferModule->merge(fReceiveBuffer, buffer, true);
|
||||
if (status < B_OK) {
|
||||
fNextByteExpected -= buffer->size;
|
||||
fReceiveNext -= buffer->size;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
} else
|
||||
fReceiveBuffer = buffer;
|
||||
} else {
|
||||
// add this buffer into the ReorderQueue in the correct place
|
||||
// creating a new tcp_segment if necessary
|
||||
|
@ -527,195 +524,198 @@ TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
|
|||
|
||||
|
||||
status_t
|
||||
TCPConnection::ReceiveData(net_buffer *buffer)
|
||||
TCPConnection::DelayedAcknowledge()
|
||||
{
|
||||
BenaphoreLocker lock(&fLock);
|
||||
TRACE(("TCP:%p.ReceiveData()\n", this));
|
||||
// TODO: use timer instead and/or piggyback on send
|
||||
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));
|
||||
switch (fState) {
|
||||
case CLOSED:
|
||||
case TIME_WAIT:
|
||||
gBufferModule->free(buffer);
|
||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE)
|
||||
return _Reset(byteAckd, 0);
|
||||
status_t
|
||||
TCPConnection::UpdateTimeWait()
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return _Reset(0, byteRcvd + dataLength);
|
||||
|
||||
case LISTEN:
|
||||
// if packet is SYN, spawn new TCPConnection in SYN_RCVD state
|
||||
// and add it to the Connection Queue. The new TCPConnection
|
||||
// must continue the handshake by replying with SYN+ACK. Any
|
||||
// data in the packet must go into the new TCPConnection's receive
|
||||
// buffer.
|
||||
// Otherwise, RST+ACK is sent.
|
||||
// The current TCPConnection always remains in LISTEN state.
|
||||
return B_ERROR;
|
||||
int32
|
||||
TCPConnection::ListenReceive(tcp_segment_header& segment, net_buffer *buffer)
|
||||
{
|
||||
// Essentially, we accept only TCP_FLAG_SYNCHRONIZE in this state,
|
||||
// but the error behaviour differs
|
||||
if (segment.flags & TCP_FLAG_RESET)
|
||||
return DROP;
|
||||
if (segment.flags & TCP_FLAG_ACKNOWLEDGE)
|
||||
return DROP | RESET;
|
||||
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) == 0)
|
||||
return DROP;
|
||||
|
||||
case SYNCHRONIZE_SENT:
|
||||
if (header.flags & TCP_FLAG_RESET) {
|
||||
fError = ECONNREFUSED;
|
||||
fState = CLOSED;
|
||||
return B_ERROR;
|
||||
}
|
||||
// TODO: drop broadcast/multicast
|
||||
|
||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE && !_IsAcknowledgeValid(byteAckd))
|
||||
return _Reset(byteAckd, 0);
|
||||
// spawn new connection for accept()
|
||||
net_socket *newSocket;
|
||||
if (gSocketModule->spawn_pending_socket(socket, &newSocket) < B_OK)
|
||||
return DROP;
|
||||
|
||||
if (header.flags & TCP_FLAG_SYNCHRONIZE) {
|
||||
fNextByteToRead = fNextByteExpected = ntohl(header.sequence_num) + 1;
|
||||
flags |= TCP_FLAG_ACKNOWLEDGE;
|
||||
fLastByteAckd = byteAckd;
|
||||
gAddressModule->set_to((sockaddr *)&newSocket->address,
|
||||
(sockaddr *)&buffer->destination);
|
||||
gAddressModule->set_to((sockaddr *)&newSocket->peer,
|
||||
(sockaddr *)&buffer->source);
|
||||
|
||||
// cancel resend of this segment
|
||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE)
|
||||
nextState = ESTABLISHED;
|
||||
else {
|
||||
nextState = SYNCHRONIZE_RECEIVED;
|
||||
flags |= TCP_FLAG_SYNCHRONIZE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
TCPConnection *connection = (TCPConnection *)newSocket->first_protocol;
|
||||
|
||||
case SYNCHRONIZE_RECEIVED:
|
||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE && _IsAcknowledgeValid(byteAckd))
|
||||
fState = ESTABLISHED;
|
||||
else
|
||||
_Reset(byteAckd, 0);
|
||||
break;
|
||||
// TODO: proper error handling!
|
||||
|
||||
default:
|
||||
// In a synchronized state.
|
||||
// first check that the received sequence number is good
|
||||
if (_IsSequenceValid(byteRcvd, dataLength)) {
|
||||
// If a valid RST was received, terminate the connection.
|
||||
if (header.flags & TCP_FLAG_RESET) {
|
||||
fError = ECONNREFUSED;
|
||||
fState = CLOSED;
|
||||
return B_ERROR;
|
||||
}
|
||||
connection->fRoute = gDatalinkModule->get_route(gDomain,
|
||||
(sockaddr *)&newSocket->address);
|
||||
if (connection->fRoute == NULL)
|
||||
return DROP;
|
||||
|
||||
if (header.flags & TCP_FLAG_ACKNOWLEDGE && _IsAcknowledgeValid(byteAckd)) {
|
||||
fLastByteAckd = byteAckd;
|
||||
if (fLastByteAckd == fNextByteToWrite) {
|
||||
if (fState == WAIT_FOR_FINISH_ACKNOWLEDGE) {
|
||||
nextState = CLOSED;
|
||||
status = hash_remove(gConnectionHash, this);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
if (fState == CLOSING) {
|
||||
nextState = TIME_WAIT;
|
||||
status = hash_remove(gConnectionHash, this);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
if (fState == FINISH_SENT) {
|
||||
nextState = FINISH_ACKNOWLEDGED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (header.flags & TCP_FLAG_FINISH) {
|
||||
// other side is closing connection; change states
|
||||
switch (fState) {
|
||||
case ESTABLISHED:
|
||||
nextState = FINISH_RECEIVED;
|
||||
fNextByteExpected++;
|
||||
break;
|
||||
case FINISH_ACKNOWLEDGED:
|
||||
nextState = TIME_WAIT;
|
||||
fNextByteExpected++;
|
||||
break;
|
||||
case FINISH_RECEIVED:
|
||||
if (fLastByteAckd == fNextByteToWrite) {
|
||||
// our FIN has been ACKd: go to TIME_WAIT
|
||||
nextState = TIME_WAIT;
|
||||
status = hash_remove(gConnectionHash, this);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
gStackModule->set_timer(&fTimer, TCP_MAX_SEGMENT_LIFETIME);
|
||||
} else
|
||||
nextState = CLOSING;
|
||||
fNextByteExpected++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
flags |= TCP_FLAG_ACKNOWLEDGE;
|
||||
} else {
|
||||
// out-of-order packet received. remind the other side of where we are
|
||||
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, true);
|
||||
}
|
||||
break;
|
||||
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;
|
||||
fState = CLOSED;
|
||||
return DROP;
|
||||
}
|
||||
TRACE(("TCP %p.ReceiveData():Entering state %d\n", this, fState));
|
||||
|
||||
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
|
||||
&& !_IsAcknowledgeValid(segment.acknowledge))
|
||||
return DROP | RESET;
|
||||
|
||||
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) == 0)
|
||||
return DROP;
|
||||
|
||||
fReceiveNext = segment.sequence + 1;
|
||||
fLastAcknowledged = segment.acknowledge;
|
||||
|
||||
if (segment.flags & TCP_FLAG_ACKNOWLEDGE) {
|
||||
// the connection has been established
|
||||
fState = ESTABLISHED;
|
||||
} else {
|
||||
// simultaneous open
|
||||
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!
|
||||
|
||||
// first check that the received sequence number is good
|
||||
if (_IsSequenceValid(segment.sequence, buffer->size)) {
|
||||
// If a valid RST was received, terminate the connection.
|
||||
if (segment.flags & TCP_FLAG_RESET) {
|
||||
fError = ECONNREFUSED;
|
||||
fState = CLOSED;
|
||||
return DROP;
|
||||
}
|
||||
}
|
||||
|
||||
int32 action = KEEP;
|
||||
status_t status;
|
||||
|
||||
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
|
||||
&& _IsAcknowledgeValid(segment.acknowledge)) {
|
||||
fLastAcknowledged = segment.acknowledge;
|
||||
if (fLastAcknowledged == fSendNext) {
|
||||
if (fState == WAIT_FOR_FINISH_ACKNOWLEDGE) {
|
||||
fState = CLOSED;
|
||||
status = remove_connection(this);
|
||||
if (status != B_OK)
|
||||
return DROP;
|
||||
} else if (fState == CLOSING) {
|
||||
fState = TIME_WAIT;
|
||||
status = remove_connection(this);
|
||||
if (status != B_OK)
|
||||
return DROP;
|
||||
} else if (fState == FINISH_SENT)
|
||||
fState = FINISH_ACKNOWLEDGED;
|
||||
}
|
||||
if (segment.flags & TCP_FLAG_FINISH) {
|
||||
fReceiveNext++;
|
||||
|
||||
// other side is closing connection; change states
|
||||
switch (fState) {
|
||||
case ESTABLISHED:
|
||||
fState = FINISH_RECEIVED;
|
||||
break;
|
||||
case FINISH_ACKNOWLEDGED:
|
||||
fState = TIME_WAIT;
|
||||
break;
|
||||
case FINISH_RECEIVED:
|
||||
if (fLastAcknowledged == fSendNext) {
|
||||
fState = TIME_WAIT;
|
||||
status = remove_connection(this);
|
||||
if (status != B_OK)
|
||||
return DROP;
|
||||
|
||||
//gStackModule->set_timer(&fTimer, TCP_MAX_SEGMENT_LIFETIME);
|
||||
} else
|
||||
fState = CLOSING;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fState != FINISH_ACKNOWLEDGED)
|
||||
action |= ACKNOWLEDGE;
|
||||
} else {
|
||||
// out-of-order packet received, remind the other side of where we are
|
||||
return DROP | ACKNOWLEDGE;
|
||||
}
|
||||
|
||||
TRACE(("TCP %p.Receive():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);
|
||||
status = _EnqueueReceivedData(buffer, segment.sequence);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
return DROP;
|
||||
} else
|
||||
gBufferModule->free(buffer);
|
||||
|
||||
if (fState != CLOSING && fState != WAIT_FOR_FINISH_ACKNOWLEDGE) {
|
||||
status = _SendQueuedData(flags, false);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
if (fState != CLOSING && fState != WAIT_FOR_FINISH_ACKNOWLEDGE)
|
||||
action &= ACKNOWLEDGE;
|
||||
|
||||
fState = nextState;
|
||||
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;
|
||||
return action;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
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.
|
||||
The fLock benaphore must be held before calling.
|
||||
The send lock must be held before calling.
|
||||
*/
|
||||
status_t
|
||||
TCPConnection::_SendQueuedData(uint16 flags, bool empty)
|
||||
|
@ -750,49 +751,51 @@ TCPConnection::_SendQueuedData(uint16 flags, bool empty)
|
|||
|
||||
net_buffer *buffer;
|
||||
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 (flags == 0) {
|
||||
// there is just nothing left to do
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
buffer = gBufferModule->create(256);
|
||||
TRACE(("TCP: Sending Buffer %p\n", buffer));
|
||||
if (buffer == NULL)
|
||||
return ENOBUFS;
|
||||
} else {
|
||||
buffer = fSendBuffer;
|
||||
if (effectiveWindow == fSendBuffer->size)
|
||||
if (effectiveWindow == fSendBuffer->size) {
|
||||
buffer = fSendBuffer;
|
||||
fSendBuffer = NULL;
|
||||
else
|
||||
fSendBuffer = gBufferModule->split(fSendBuffer, effectiveWindow);
|
||||
} else
|
||||
buffer = gBufferModule->split(fSendBuffer, effectiveWindow);
|
||||
}
|
||||
|
||||
gAddressModule->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address);
|
||||
gAddressModule->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer);
|
||||
TRACE(("TCP:%p.SendQueuedData() to address %s\n", this,
|
||||
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data()));
|
||||
TRACE(("TCP:%p.SendQueuedData() from address %s\n", this,
|
||||
TRACE(("TCP:%p.SendQueuedData() buffer %p, from address %s to %s\n", this,
|
||||
buffer,
|
||||
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data(),
|
||||
AddressString(gDomain, (sockaddr *)&buffer->source, true).Data()));
|
||||
|
||||
uint16 advertisedWindow = TCP_MAX_RECV_BUF - (fNextByteExpected - fNextByteToRead);
|
||||
uint32 size = buffer->size;
|
||||
|
||||
status_t status = add_tcp_header(buffer, flags, fNextByteToSend,
|
||||
fNextByteExpected, advertisedWindow);
|
||||
status_t status = add_tcp_header(buffer, flags, fSendNext,
|
||||
fReceiveNext, fReceiveWindow);
|
||||
if (status != B_OK) {
|
||||
gBufferModule->free(buffer);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Only count 1 SYN, the 1 sent when transitioning from CLOSED or LISTEN
|
||||
if ((flags & TCP_FLAG_SYNCHRONIZE) != 0 && (fState == CLOSED || fState == LISTEN))
|
||||
fNextByteToSend++;
|
||||
if ((flags & TCP_FLAG_SYNCHRONIZE) != 0)
|
||||
fSendNext++;
|
||||
|
||||
// Only count 1 FIN, the 1 sent when transitioning from
|
||||
// ESTABLISHED, SYNCHRONIZE_RECEIVED or FINISH_RECEIVED
|
||||
if ((flags & TCP_FLAG_FINISH) != 0
|
||||
&& (fState == SYNCHRONIZE_RECEIVED || fState == ESTABLISHED || fState == FINISH_RECEIVED))
|
||||
fNextByteToSend++;
|
||||
if ((flags & TCP_FLAG_FINISH) != 0)
|
||||
fSendNext++;
|
||||
|
||||
fNextByteToSend += size;
|
||||
fSendNext += size;
|
||||
|
||||
#if 0
|
||||
tcp_segment *segment = new(std::nothrow)
|
||||
|
@ -813,7 +816,7 @@ int
|
|||
TCPConnection::Compare(void *_connection, const void *_key)
|
||||
{
|
||||
const tcp_connection_key *key = (tcp_connection_key *)_key;
|
||||
TCPConnection *connection= ((TCPConnection *)_connection);
|
||||
TCPConnection *connection = ((TCPConnection *)_connection);
|
||||
|
||||
if (gAddressModule->equal_addresses_and_ports(key->local,
|
||||
(sockaddr *)&connection->socket->address)
|
||||
|
@ -830,11 +833,10 @@ TCPConnection::Hash(void *_connection, const void *_key, uint32 range)
|
|||
{
|
||||
if (_connection != NULL) {
|
||||
TCPConnection *connection = (TCPConnection *)_connection;
|
||||
return gAddressModule->hash_address_pair(
|
||||
(sockaddr *)&connection->socket->address, (sockaddr *)&connection->socket->peer) % range;
|
||||
return gAddressModule->hash_address_pair((sockaddr *)&connection->socket->address,
|
||||
(sockaddr *)&connection->socket->peer) % range;
|
||||
}
|
||||
|
||||
const tcp_connection_key *key = (tcp_connection_key *)_key;
|
||||
return gAddressModule->hash_address_pair(
|
||||
key->local, key->peer) % range;
|
||||
return gAddressModule->hash_address_pair(key->local, key->peer) % range;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ class TCPConnection : public net_protocol {
|
|||
TCPConnection(net_socket *socket);
|
||||
~TCPConnection();
|
||||
|
||||
status_t InitCheck() const;
|
||||
|
||||
status_t Open();
|
||||
status_t Close();
|
||||
status_t Free();
|
||||
|
@ -33,17 +35,24 @@ class TCPConnection : public net_protocol {
|
|||
status_t Listen(int count);
|
||||
status_t Shutdown(int direction);
|
||||
status_t SendData(net_buffer *buffer);
|
||||
status_t SendRoutedData(net_route *route, net_buffer *buffer);
|
||||
size_t SendAvailable();
|
||||
status_t ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer);
|
||||
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 int Compare(void *_packet, const void *_key);
|
||||
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:
|
||||
bool _IsAcknowledgeValid(uint32 acknowledge);
|
||||
|
@ -51,34 +60,38 @@ class TCPConnection : public net_protocol {
|
|||
|
||||
status_t _SendQueuedData(uint16 flags, bool empty);
|
||||
status_t _EnqueueReceivedData(net_buffer *buffer, uint32 sequenceNumber);
|
||||
status_t _Reset(uint32 sequenceNum, uint32 acknowledgeNum);
|
||||
|
||||
static void _TimeWait(struct net_timer *timer, void *data);
|
||||
|
||||
uint32 fLastByteAckd;
|
||||
uint32 fNextByteToSend;
|
||||
uint32 fNextByteToWrite;
|
||||
TCPConnection *fHashNext;
|
||||
|
||||
uint32 fNextByteToRead;
|
||||
uint32 fNextByteExpected;
|
||||
uint32 fLastByteReceived;
|
||||
|
||||
bigtime_t fAvgRTT;
|
||||
benaphore fSendLock;
|
||||
benaphore fReceiveLock;
|
||||
benaphore fLock;
|
||||
sem_id fAcceptSemaphore;
|
||||
|
||||
uint32 fLastAcknowledged;
|
||||
uint32 fSendNext;
|
||||
uint32 fSendWindow;
|
||||
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;
|
||||
|
||||
tcp_state fState;
|
||||
status_t fError;
|
||||
vint32 fDelayedAcknowledge;
|
||||
|
||||
struct list fReorderQueue;
|
||||
struct list fWaitQueue;
|
||||
|
||||
TCPConnection *fHashLink;
|
||||
tcp_state fState;
|
||||
status_t fError;
|
||||
benaphore fLock;
|
||||
// timer
|
||||
net_timer fTimer;
|
||||
|
||||
net_route *fRoute;
|
||||
// TODO: don't use a net_route, but a net_route_info!!!
|
||||
};
|
||||
|
||||
#endif // TCP_CONNECTION_H
|
||||
|
|
|
@ -44,9 +44,10 @@ net_domain *gDomain;
|
|||
net_address_module_info *gAddressModule;
|
||||
net_buffer_module_info *gBufferModule;
|
||||
net_datalink_module_info *gDatalinkModule;
|
||||
net_socket_module_info *gSocketModule;
|
||||
net_stack_module_info *gStackModule;
|
||||
hash_table *gConnectionHash;
|
||||
benaphore gConnectionLock;
|
||||
recursive_lock gConnectionLock;
|
||||
|
||||
|
||||
#ifdef TRACE_TCP
|
||||
|
@ -55,7 +56,7 @@ benaphore gConnectionLock;
|
|||
void
|
||||
tcp_dump_hash()
|
||||
{
|
||||
BenaphoreLocker lock(&gConnectionLock);
|
||||
RecursiveLocker lock(&gConnectionLock);
|
||||
if (gDomain == NULL) {
|
||||
TRACE(("Unable to dump TCP Connections!\n"));
|
||||
return;
|
||||
|
@ -66,9 +67,9 @@ tcp_dump_hash()
|
|||
hash_rewind(gConnectionHash, &iterator);
|
||||
TRACE(("Active TCP Connections:\n"));
|
||||
while ((connection = (TCPConnection *)hash_next(gConnectionHash, &iterator)) != NULL) {
|
||||
TRACE((" TCPConnection %p: %s, %s\n", connection,
|
||||
AddressString(gDomain, (sockaddr *)&connection->socket->address, true).Data(),
|
||||
AddressString(gDomain, (sockaddr *)&connection->socket->peer, true).Data()));
|
||||
TRACE((" TCPConnection %p: local %s, peer %s\n", connection,
|
||||
AddressString(gDomain, (sockaddr *)&connection->socket->address, true).Data(),
|
||||
AddressString(gDomain, (sockaddr *)&connection->socket->peer, true).Data()));
|
||||
}
|
||||
hash_close(gConnectionHash, &iterator, false);
|
||||
}
|
||||
|
@ -104,8 +105,8 @@ set_domain(net_interface *interface = NULL)
|
|||
for \a flags, \a seq \a ack and \a advertisedWindow.
|
||||
*/
|
||||
status_t
|
||||
add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence, uint32 ack,
|
||||
uint16 advertisedWindow)
|
||||
add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence,
|
||||
uint32 acknowledge, uint16 advertisedWindow)
|
||||
{
|
||||
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.destination_port = gAddressModule->get_port((sockaddr *)&buffer->destination);
|
||||
header.sequence_num = htonl(sequence);
|
||||
header.acknowledge_num = htonl(ack);
|
||||
header.sequence = htonl(sequence);
|
||||
header.acknowledge = (flags & TCP_FLAG_ACKNOWLEDGE) ? htonl(acknowledge) : 0;
|
||||
header.reserved = 0;
|
||||
header.header_length = 5;
|
||||
header.header_length = sizeof(tcp_header) >> 2;
|
||||
// currently no options supported
|
||||
header.flags = (uint8)flags;
|
||||
header.advertised_window = htons(advertisedWindow);
|
||||
header.checksum = 0;
|
||||
header.urgent_ptr = 0;
|
||||
// urgent pointer not supported
|
||||
header.urgent_offset = 0;
|
||||
// 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
|
||||
Checksum checksum;
|
||||
|
@ -137,20 +141,134 @@ add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence, uint32 ack,
|
|||
<< (uint16)htons(buffer->size)
|
||||
<< Checksum::BufferHelper(buffer, gBufferModule);
|
||||
header.checksum = checksum;
|
||||
TRACE(("TCP: Checksum for segment %p is %X\n", buffer, header.checksum));
|
||||
|
||||
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
|
||||
|
||||
|
||||
net_protocol *
|
||||
tcp_init_protocol(net_socket *socket)
|
||||
{
|
||||
DUMP_TCP_HASH;
|
||||
socket->protocol = IPPROTO_TCP;
|
||||
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));
|
||||
return protocol;
|
||||
}
|
||||
|
@ -259,7 +377,8 @@ status_t
|
|||
tcp_send_routed_data(net_protocol *protocol, struct net_route *route,
|
||||
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_connection_key key;
|
||||
key.peer = (struct sockaddr *)&buffer->source;
|
||||
key.local = (struct sockaddr *)&buffer->destination;
|
||||
uint16 headerLength = header.HeaderLength();
|
||||
if (headerLength < sizeof(tcp_header))
|
||||
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->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;
|
||||
|
||||
BenaphoreLocker hashLock(&gConnectionLock);
|
||||
TCPConnection *connection = (TCPConnection *)hash_lookup(gConnectionHash, &key);
|
||||
TRACE(("TCP: Received packet corresponds to connection %p\n", connection));
|
||||
if (connection != NULL){
|
||||
return connection->ReceiveData(buffer);
|
||||
} else {
|
||||
/* TODO:
|
||||
No explicit connection exists. Check for wildcard connections:
|
||||
First check if any connections exist where local = IPADDR_ANY
|
||||
then check when local = peer = IPADDR_ANY.
|
||||
port numbers always remain the same */
|
||||
// TODO: process options!
|
||||
tcp_segment_header segment;
|
||||
segment.sequence = header.Sequence();
|
||||
segment.acknowledge = header.Acknowledge();
|
||||
segment.advertised_window = header.AdvertisedWindow();
|
||||
segment.urgent_offset = header.UrgentOffset();
|
||||
segment.flags = header.flags;
|
||||
|
||||
// If no connection exists (and RESET is not set) send RESET
|
||||
if ((header.flags & TCP_FLAG_RESET) == 0) {
|
||||
TRACE(("TCP: Connection does not exist!\n"));
|
||||
net_buffer *reply = gBufferModule->create(512);
|
||||
if (reply == NULL)
|
||||
return B_NO_MEMORY;
|
||||
bufferHeader.Remove(headerLength);
|
||||
// we no longer need to keep the header around
|
||||
|
||||
gAddressModule->set_to((sockaddr *)&reply->source,
|
||||
(sockaddr *)&buffer->destination);
|
||||
gAddressModule->set_to((sockaddr *)&reply->destination,
|
||||
(sockaddr *)&buffer->source);
|
||||
RecursiveLocker hashLock(&gConnectionLock);
|
||||
int32 segmentAction = DROP;
|
||||
|
||||
TCPConnection *connection = find_connection((struct sockaddr *)&buffer->destination,
|
||||
(struct sockaddr *)&buffer->source);
|
||||
if (connection != NULL) {
|
||||
switch (connection->State()) {
|
||||
case CLOSED:
|
||||
case TIME_WAIT:
|
||||
connection->UpdateTimeWait();
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
case LISTEN:
|
||||
segmentAction = connection->ListenReceive(segment, buffer);
|
||||
break;
|
||||
|
||||
status_t status = add_tcp_header(reply, flags, sequence, acknowledge, 0);
|
||||
case SYNCHRONIZE_SENT:
|
||||
segmentAction = connection->SynchronizeSentReceive(segment, buffer);
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
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"));
|
||||
|
||||
reply_with_reset(segment, buffer);
|
||||
}
|
||||
if (segmentAction & DROP)
|
||||
gBufferModule->free(buffer);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
@ -410,46 +551,51 @@ tcp_init()
|
|||
status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule);
|
||||
if (status < B_OK)
|
||||
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)
|
||||
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(),
|
||||
&TCPConnection::Compare, &TCPConnection::Hash);
|
||||
if (gConnectionHash == NULL)
|
||||
goto err3;
|
||||
|
||||
status = benaphore_init(&gConnectionLock, "TCP Hash Lock");
|
||||
if (status < B_OK)
|
||||
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,
|
||||
"network/protocols/tcp/v1",
|
||||
"network/protocols/ipv4/v1",
|
||||
NULL);
|
||||
if (status < B_OK)
|
||||
goto err5;
|
||||
goto err6;
|
||||
|
||||
status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||||
"network/protocols/tcp/v1",
|
||||
"network/protocols/ipv4/v1",
|
||||
NULL);
|
||||
if (status < B_OK)
|
||||
goto err5;
|
||||
goto err6;
|
||||
|
||||
status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP,
|
||||
"network/protocols/tcp/v1");
|
||||
if (status < B_OK)
|
||||
goto err5;
|
||||
goto err6;
|
||||
|
||||
return B_OK;
|
||||
|
||||
err6:
|
||||
recursive_lock_destroy(&gConnectionLock);
|
||||
err5:
|
||||
benaphore_destroy(&gConnectionLock);
|
||||
err4:
|
||||
hash_uninit(gConnectionHash);
|
||||
err3:
|
||||
err4:
|
||||
put_module(NET_DATALINK_MODULE_NAME);
|
||||
err3:
|
||||
put_module(NET_SOCKET_MODULE_NAME);
|
||||
err2:
|
||||
put_module(NET_BUFFER_MODULE_NAME);
|
||||
err1:
|
||||
|
@ -463,9 +609,10 @@ err1:
|
|||
static status_t
|
||||
tcp_uninit()
|
||||
{
|
||||
benaphore_destroy(&gConnectionLock);
|
||||
recursive_lock_destroy(&gConnectionLock);
|
||||
hash_uninit(gConnectionHash);
|
||||
put_module(NET_DATALINK_MODULE_NAME);
|
||||
put_module(NET_SOCKET_MODULE_NAME);
|
||||
put_module(NET_BUFFER_MODULE_NAME);
|
||||
put_module(NET_STACK_MODULE_NAME);
|
||||
|
||||
|
|
|
@ -11,13 +11,18 @@
|
|||
|
||||
#include <net_buffer.h>
|
||||
#include <net_datalink.h>
|
||||
#include <net_socket.h>
|
||||
#include <net_stack.h>
|
||||
|
||||
#include <util/khash.h>
|
||||
|
||||
#include <ByteOrder.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
|
||||
class TCPConnection;
|
||||
|
||||
typedef enum {
|
||||
// establishing a connection
|
||||
CLOSED,
|
||||
|
@ -40,8 +45,8 @@ typedef enum {
|
|||
struct tcp_header {
|
||||
uint16 source_port;
|
||||
uint16 destination_port;
|
||||
uint32 sequence_num;
|
||||
uint32 acknowledge_num;
|
||||
uint32 sequence;
|
||||
uint32 acknowledge;
|
||||
struct {
|
||||
#if B_HOST_IS_LENDIAN == 1
|
||||
uint8 reserved : 4;
|
||||
|
@ -54,7 +59,14 @@ struct tcp_header {
|
|||
uint8 flags;
|
||||
uint16 advertised_window;
|
||||
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
|
||||
|
@ -67,6 +79,22 @@ struct tcp_header {
|
|||
#define TCP_FLAG_ECN 0x40 // Explicit Congestion Notification echo
|
||||
#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 {
|
||||
const sockaddr *local;
|
||||
const sockaddr *peer;
|
||||
|
@ -77,12 +105,15 @@ extern net_domain *gDomain;
|
|||
extern net_address_module_info *gAddressModule;
|
||||
extern net_buffer_module_info *gBufferModule;
|
||||
extern net_datalink_module_info *gDatalinkModule;
|
||||
extern net_socket_module_info *gSocketModule;
|
||||
extern net_stack_module_info *gStackModule;
|
||||
extern hash_table *gConnectionHash;
|
||||
extern benaphore gConnectionLock;
|
||||
//extern hash_table *gConnectionHash;
|
||||
//extern benaphore gConnectionLock;
|
||||
|
||||
|
||||
status_t add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence,
|
||||
uint32 ack, uint16 advertisedWindow);
|
||||
status_t remove_connection(TCPConnection *connection);
|
||||
status_t insert_connection(TCPConnection *connection);
|
||||
|
||||
#endif // TCP_H
|
||||
|
|
Loading…
Reference in New Issue