From 30ea523ad10945ebf4562595952e78f9ae95878d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 19 Nov 2006 23:33:24 +0000 Subject: [PATCH] * 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 --- .../network/protocols/tcp/TCPConnection.cpp | 638 +++++++++--------- .../network/protocols/tcp/TCPConnection.h | 51 +- .../kernel/network/protocols/tcp/tcp.cpp | 295 ++++++-- .../kernel/network/protocols/tcp/tcp.h | 41 +- 4 files changed, 609 insertions(+), 416 deletions(-) diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp index 1edd3092a3..c711d7127a 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp @@ -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 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; } diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h index 9a885dbbf7..18a761a04a 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h +++ b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h @@ -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 diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index b4d408b051..2713813e3e 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -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); diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.h b/src/add-ons/kernel/network/protocols/tcp/tcp.h index c41d3c7af4..7cad032f69 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.h +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.h @@ -11,13 +11,18 @@ #include #include +#include #include #include +#include + #include +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