diff --git a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp new file mode 100644 index 0000000000..04d783b48c --- /dev/null +++ b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp @@ -0,0 +1,400 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ + + +#include "EndpointManager.h" +#include "TCPConnection.h" + +#include + +#include + +#include + + +#define TRACE_ENDPOINT_MANAGER +#ifdef TRACE_ENDPOINT_MANAGER +# define TRACE(x) dprintf x +#else +# define TRACE(x) +#endif + + +struct connection_key { + const sockaddr *local; + const sockaddr *peer; +}; + +struct endpoint_key { + uint16 port; +}; + + +static const uint32 kConnectionHashBuckets = 256; +static const uint32 kEndpointHashBuckets = 256; + +static const uint16 kLastReservedPort = 1023; +static const uint16 kFirstEphemeralPort = 40000; + + +EndpointManager::EndpointManager() +{ + fConnectionHash = hash_init(kConnectionHashBuckets, + offsetof(TCPConnection, fConnectionHashNext), + &_ConnectionCompare, &_ConnectionHash); + fEndpointHash = hash_init(kEndpointHashBuckets, + offsetof(TCPConnection, fEndpointHashNext), + &_EndpointCompare, &_EndpointHash); + + recursive_lock_init(&fLock, "endpoint manager"); +} + + +EndpointManager::~EndpointManager() +{ + hash_uninit(fConnectionHash); + hash_uninit(fEndpointHash); + + recursive_lock_destroy(&fLock); +} + + +status_t +EndpointManager::InitCheck() const +{ + if (fConnectionHash == NULL + || fEndpointHash == NULL) + return B_NO_MEMORY; + + if (fLock.sem < B_OK) + return fLock.sem; + + return B_OK; +} + + +// #pragma mark - connections + + +/*! + Returns the endpoint matching the connection. + You must hold the manager's lock when calling this method. +*/ +TCPConnection * +EndpointManager::_LookupConnection(sockaddr *local, sockaddr *peer) +{ + connection_key key; + key.local = local; + key.peer = peer; + + return (TCPConnection *)hash_lookup(fConnectionHash, &key); +} + + +status_t +EndpointManager::_RemoveConnection(TCPConnection *endpoint) +{ + RecursiveLocker locker(&fLock); + return hash_remove(fConnectionHash, endpoint); +} + + +void +EndpointManager::_DumpConnections() +{ + RecursiveLocker lock(&fLock); + + if (gDomain == NULL) + return; + + struct hash_iterator iterator; + hash_open(fConnectionHash, &iterator); + + TRACE(("Active TCP Connections:\n")); + + TCPConnection *endpoint; + while ((endpoint = (TCPConnection *)hash_next(fConnectionHash, &iterator)) != NULL) { + TRACE((" TCPConnection %p: local %s, peer %s\n", endpoint, + AddressString(gDomain, (sockaddr *)&endpoint->socket->address, true).Data(), + AddressString(gDomain, (sockaddr *)&endpoint->socket->peer, true).Data())); + } + + hash_close(fConnectionHash, &iterator, false); +} + + +status_t +EndpointManager::SetConnection(TCPConnection *endpoint, + const sockaddr *local, const sockaddr *peer, const sockaddr *interfaceLocal) +{ + RecursiveLocker locker(&fLock); + sockaddr localBuffer; + + // need to associate this connection with a real address, not INADDR_ANY + if (gAddressModule->is_empty_address(local, false)) { + gAddressModule->set_to(&localBuffer, interfaceLocal); + gAddressModule->set_port(&localBuffer, gAddressModule->get_port(local)); + local = &localBuffer; + } + + connection_key key; + key.local = local; + key.peer = peer; + + if (hash_lookup(fConnectionHash, &key) != NULL) + return EADDRINUSE; + + _RemoveConnection(endpoint); + + gAddressModule->set_to((sockaddr *)&endpoint->socket->address, local); + gAddressModule->set_to((sockaddr *)&endpoint->socket->peer, peer); + + return hash_insert(fConnectionHash, endpoint); +} + + +TCPConnection * +EndpointManager::FindConnection(sockaddr *local, sockaddr *peer) +{ + TCPConnection *endpoint = _LookupConnection(local, peer); + if (endpoint != NULL) { + TRACE(("TCP: Received packet corresponds to explicit endpoint %p\n", endpoint)); + return endpoint; + } + + // no explicit endpoint exists, check for wildcard endpoints + + sockaddr wildcard; + gAddressModule->set_to_empty_address(&wildcard); + + endpoint = _LookupConnection(local, &wildcard); + if (endpoint != NULL) { + TRACE(("TCP: Received packet corresponds to wildcard endpoint %p\n", endpoint)); + return endpoint; + } + + sockaddr localWildcard; + gAddressModule->set_to_empty_address(&localWildcard); + gAddressModule->set_port(&localWildcard, gAddressModule->get_port(local)); + + endpoint = _LookupConnection(&localWildcard, &wildcard); + if (endpoint != NULL) { + TRACE(("TCP: Received packet corresponds to local wildcard endpoint %p\n", endpoint)); + return endpoint; + } + + // no matching endpoint exists + TRACE(("TCP: no matching endpoint!\n")); + _DumpConnections(); + + return NULL; +} + + +// #pragma mark - endpoints + + +TCPConnection * +EndpointManager::_LookupEndpoint(uint16 port) +{ + endpoint_key key; + key.port = port; + + return (TCPConnection *)hash_lookup(fEndpointHash, &key); +} + + +status_t +EndpointManager::Bind(TCPConnection *endpoint, sockaddr *address) +{ + if (gAddressModule->is_empty_address(address, true)) + return B_BAD_VALUE; + + uint16 port = gAddressModule->get_port(address); + + // TODO: check the root group instead? + if (ntohs(port) <= kLastReservedPort && geteuid() != 0) + return B_PERMISSION_DENIED; + + RecursiveLocker locker(&fLock); + + TCPConnection *first = _LookupEndpoint(port); + + // If there is already an endpoint bound to that port, SO_REUSEADDR has to be + // specified by the new endpoint to be allowed to bind to that same port. + // Alternatively, all endpoints must have the SO_REUSEPORT option set. + if (first != NULL + && (endpoint->socket->options & SO_REUSEADDR) == 0 + && ((endpoint->socket->options & SO_REUSEPORT) == 0 + || (first->socket->options & SO_REUSEPORT) == 0)) + return EADDRINUSE; + + if (first != NULL) { + TCPConnection *last = first; + while (true) { + // check if this endpoint binds to a wildcard address + if (gAddressModule->is_empty_address((sockaddr *)&last->socket->address, false)) { + // you cannot specialize a wildcard endpoint - you have to open the + // wildcard endpoint last + return B_PERMISSION_DENIED; + } + + if (last->fEndpointNextWithSamePort == NULL) + break; + + last = last->fEndpointNextWithSamePort; + } + + // "first" stays the first item in the list + last->fEndpointNextWithSamePort = endpoint; + } else + hash_insert(fEndpointHash, endpoint); + + endpoint->fEndpointNextWithSamePort = NULL; + hash_insert(fConnectionHash, endpoint); + + return B_OK; +} + + +status_t +EndpointManager::BindToEphemeral(TCPConnection *endpoint, sockaddr *address) +{ + RecursiveLocker locker(&fLock); + + uint32 max = kFirstEphemeralPort + 65536; + + for (int32 i = 1; i < 5; i++) { + // try to retrieve a more or less random port + uint32 counter = kFirstEphemeralPort; + uint32 step = i == 4 ? 1 : system_time() & 0x1f; + + while (counter < max) { + uint16 port = counter & 0xffff; + if (port <= kLastReservedPort) + port += kLastReservedPort; + + port = htons(port); + + TCPConnection *other = _LookupEndpoint(port); + if (other == NULL) { + // found a port + gAddressModule->set_port((sockaddr *)&endpoint->socket->address, port); + hash_insert(fEndpointHash, endpoint); + hash_insert(fConnectionHash, endpoint); + return B_OK; + } + + counter += step; + } + } + + // could not find a port! + return EADDRINUSE; +} + + +status_t +EndpointManager::Unbind(TCPConnection *endpoint) +{ + if (endpoint == NULL || !endpoint->IsBound()) + return B_BAD_VALUE; + + RecursiveLocker locker(&fLock); + + if (!endpoint->IsBound()) + return B_BAD_VALUE; + + TCPConnection *other = _LookupEndpoint(gAddressModule->get_port((sockaddr *)&endpoint->socket->address)); + if (other != endpoint) { + // remove endpoint from the list of endpoints with the same port + while (other->fEndpointNextWithSamePort != endpoint) { + other = other->fEndpointNextWithSamePort; + } + + other->fEndpointNextWithSamePort = endpoint->fEndpointNextWithSamePort; + } else { + // we need to replace the first endpoint in the list + hash_remove(fEndpointHash, endpoint); + + other = endpoint->fEndpointNextWithSamePort; + if (other != NULL) + hash_insert(fEndpointHash, other); + } + + endpoint->fEndpointNextWithSamePort = NULL; + _RemoveConnection(endpoint); + + endpoint->socket->address.ss_len = 0; + + return B_OK; +} + + +// #pragma mark - hash functions + + +/*static*/ int +EndpointManager::_ConnectionCompare(void *_endpoint, const void *_key) +{ + const connection_key *key = (connection_key *)_key; + TCPConnection *endpoint = (TCPConnection *)_endpoint; + + if (gAddressModule->equal_addresses_and_ports(key->local, + (sockaddr *)&endpoint->socket->address) + && gAddressModule->equal_addresses_and_ports(key->peer, + (sockaddr *)&endpoint->socket->peer)) + return 0; + + return 1; +} + + +/*static*/ uint32 +EndpointManager::_ConnectionHash(void *_endpoint, const void *_key, uint32 range) +{ + const sockaddr *local; + const sockaddr *peer; + + if (_endpoint != NULL) { + TCPConnection *endpoint = (TCPConnection *)_endpoint; + local = (sockaddr *)&endpoint->socket->address; + peer = (sockaddr *)&endpoint->socket->peer; + } else { + const connection_key *key = (connection_key *)_key; + local = key->local; + peer = key->peer; + } + + return gAddressModule->hash_address_pair(local, peer) % range; +} + + +/*static*/ int +EndpointManager::_EndpointCompare(void *_endpoint, const void *_key) +{ + const endpoint_key *key = (endpoint_key *)_key; + TCPConnection *endpoint = (TCPConnection *)_endpoint; + + return gAddressModule->get_port((sockaddr *)&endpoint->socket->address) + == key->port ? 0 : 1; +} + + +/*static*/ uint32 +EndpointManager::_EndpointHash(void *_endpoint, const void *_key, uint32 range) +{ + if (_endpoint != NULL) { + TCPConnection *endpoint = (TCPConnection *)_endpoint; + return gAddressModule->get_port((sockaddr *)&endpoint->socket->address) % range; + } + + const endpoint_key *key = (endpoint_key *)_key; + return key->port % range; +} + diff --git a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h new file mode 100644 index 0000000000..6adbc90b71 --- /dev/null +++ b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h @@ -0,0 +1,53 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef ENDPOINT_MANAGER_H +#define ENDPOINT_MANAGER_H + + +#include +#include + +#include + + +class TCPConnection; + +class EndpointManager { + public: + EndpointManager(); + ~EndpointManager(); + + status_t InitCheck() const; + + recursive_lock *Locker() { return &fLock; } + + status_t SetConnection(TCPConnection *endpoint, const sockaddr *local, + const sockaddr *peer, const sockaddr *interfaceLocal); + TCPConnection *FindConnection(sockaddr *local, sockaddr *peer); + + status_t Bind(TCPConnection *endpoint, sockaddr *address); + status_t BindToEphemeral(TCPConnection *endpoint, sockaddr *address); + status_t Unbind(TCPConnection *endpoint); + + private: + TCPConnection *_LookupConnection(sockaddr *local, sockaddr *peer); + status_t _RemoveConnection(TCPConnection *endpoint); + TCPConnection *_LookupEndpoint(uint16 port); + void _DumpConnections(); + + static int _ConnectionCompare(void *_endpoint, const void *_key); + static uint32 _ConnectionHash(void *_endpoint, const void *_key, uint32 range); + static int _EndpointCompare(void *_endpoint, const void *_key); + static uint32 _EndpointHash(void *_endpoint, const void *_key, uint32 range); + + hash_table *fConnectionHash; + hash_table *fEndpointHash; + recursive_lock fLock; +}; + +#endif // ENDPOINT_MANAGER_H diff --git a/src/add-ons/kernel/network/protocols/tcp/Jamfile b/src/add-ons/kernel/network/protocols/tcp/Jamfile index 7fa0797498..d5862f5f20 100644 --- a/src/add-ons/kernel/network/protocols/tcp/Jamfile +++ b/src/add-ons/kernel/network/protocols/tcp/Jamfile @@ -16,6 +16,7 @@ KernelAddon tcp : tcp.cpp TCPConnection.cpp BufferQueue.cpp + EndpointManager.cpp ; # Installation diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp index 403801ebea..9987b37157 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp @@ -9,12 +9,19 @@ #include "TCPConnection.h" +#include "EndpointManager.h" #include #include +#include +#include + +#include +#include +#include +#include #include -#include #include #include @@ -23,13 +30,6 @@ #include #include -#include -#include -#include - -#include -#include - // Things this implementation currently doesn't implement: // TCP, RFC 793 @@ -197,7 +197,7 @@ TCPConnection::Connect(const struct sockaddr *address) if (address->sa_family != AF_INET) return EAFNOSUPPORT; - //BenaphoreLocker lock(&fSendLock); + RecursiveLocker locker(&fLock); TRACE((" TCP: Connect(): in state %d\n", fState)); @@ -218,24 +218,9 @@ TCPConnection::Connect(const struct sockaddr *address) return ENETUNREACH; } - remove_connection(this); - // we need to temporarily remove us from the connection list, as we're - // changing our addresses - - // 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")); - uint16 port = gAddressModule->get_port((sockaddr *)&socket->address); - gAddressModule->set_to((sockaddr *)&socket->address, - (sockaddr *)fRoute->interface->address); - gAddressModule->set_port((sockaddr *)&socket->address, port); - // need to reset the port after overwriting the address - } - - gAddressModule->set_to((sockaddr *)&socket->peer, address); - // make sure connection does not already exist - status_t status = insert_connection(this); + status_t status = gEndpointManager->SetConnection(this, + (sockaddr *)&socket->address, address, fRoute->interface->address); if (status < B_OK) { TRACE((" TCP: Connect(): could not add connection: %s!\n", strerror(status))); return status; @@ -251,7 +236,6 @@ TCPConnection::Connect(const struct sockaddr *address) && (0xffffUL << fReceiveWindowShift) < socket->receive.buffer_size) { fReceiveWindowShift++; } -dprintf("************************* size = %ld, shift = %d\n", socket->receive.buffer_size, fReceiveWindowShift); TRACE((" TCP: Connect(): starting 3-way handshake...\n")); @@ -274,6 +258,8 @@ dprintf("************************* size = %ld, shift = %d\n", socket->receive.bu return EINPROGRESS; } + locker.Unlock(); + status = acquire_sem_etc(fSendLock, 1, B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, timeout); TRACE((" TCP: Connect(): Connection complete: %s\n", strerror(status))); @@ -308,11 +294,7 @@ TCPConnection::Bind(sockaddr *address) TRACE(("TCP:%p.Bind() on address %s\n", this, AddressString(gDomain, address, true).Data())); - if (address->sa_family != AF_INET) - return EAFNOSUPPORT; - - //BenaphoreLocker lock(&fSendLock); - // TODO: there is no lock yet for these things... + RecursiveLocker lock(fLock); if (fState != CLOSED) return EISCONN; @@ -322,21 +304,10 @@ TCPConnection::Bind(sockaddr *address) if (status < B_OK) return status; - gAddressModule->set_to((sockaddr *)&socket->address, address); - - 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++)); - - TRACE(("TCP:%p.Bind() on address with ephemeral port %s, peer %s\n", this, - AddressString(gDomain, (sockaddr *)&socket->address, true).Data(), - AddressString(gDomain, (sockaddr *)&socket->peer, true).Data())); - status = insert_connection(this); - } else { - status = insert_connection(this); - } + if (gAddressModule->get_port(address) == 0) + status = gEndpointManager->BindToEphemeral(this, address); + else + status = gEndpointManager->Bind(this, address); return status; } @@ -345,18 +316,10 @@ TCPConnection::Bind(sockaddr *address) status_t TCPConnection::Unbind(struct sockaddr *address) { - TRACE(("TCP:%p.Unbind()\n", this )); + TRACE(("TCP:%p.Unbind()\n", this)); - //BenaphoreLocker lock(&fSendLock); - // TODO: there is no lock yet for these things... - - status_t status = remove_connection(this); - if (status != B_OK) - return status; - - gAddressModule->set_to_empty_address((sockaddr *)&socket->address); - gAddressModule->set_port((sockaddr *)&socket->address, 0); - return B_OK; + RecursiveLocker lock(fLock); + return gEndpointManager->Unbind(this); } @@ -364,9 +327,13 @@ status_t TCPConnection::Listen(int count) { TRACE(("TCP:%p.Listen()\n", this)); - //BenaphoreLocker lock(&fSendLock); + + RecursiveLocker lock(fLock); + if (fState != CLOSED) return B_BAD_VALUE; + if (!IsBound()) + return EDESTADDRREQ; fAcceptSemaphore = create_sem(0, "tcp accept"); fState = LISTEN; @@ -504,6 +471,13 @@ TCPConnection::ReadAvailable() // #pragma mark - misc +bool +TCPConnection::IsBound() const +{ + return !gAddressModule->is_empty_address((sockaddr *)&socket->address, true); +} + + status_t TCPConnection::DelayedAcknowledge() { @@ -581,7 +555,8 @@ TCPConnection::ListenReceive(tcp_segment_header &segment, net_buffer *buffer) if (connection->fRoute == NULL) return DROP; - if (insert_connection(connection) < B_OK) + if (gEndpointManager->SetConnection(connection, (sockaddr *)&buffer->destination, + (sockaddr *)&buffer->source, NULL) < B_OK) return DROP; connection->fInitialReceiveSequence = segment.sequence; @@ -1041,7 +1016,6 @@ TCPConnection::_SendQueued(bool force) uint32 sendWindow = fSendWindow; uint32 available = fSendQueue.Available(fSendNext); bool outstandingAcknowledge = fSendMax != fSendUnacknowledged; -dprintf("fSendWindow = %lu, available = %lu, fSendNext = %lu, fSendUnacknowledged = %lu\n", fSendWindow, available, (uint32)fSendNext, (uint32)fSendUnacknowledged); if (force && sendWindow == 0 && fSendNext <= fSendQueue.LastSequence()) { // send one byte of data to ask for a window update @@ -1073,7 +1047,6 @@ dprintf("fSendWindow = %lu, available = %lu, fSendNext = %lu, fSendUnacknowledge segment.urgent_offset = 0; while (true) { -dprintf("length = %ld, segmentLength = %lu\n", length, segmentLength); // Determine if we should really send this segment if (!force && !_ShouldSendSegment(segment, segmentLength, outstandingAcknowledge)) { if (fSendQueue.Available() @@ -1127,6 +1100,9 @@ dprintf("length = %ld, segmentLength = %lu\n", length, segmentLength); return status; } + // TODO: we need to trim the segment to the max segment size in case + // the options made it too large + status = next->module->send_routed_data(next, fRoute, buffer); if (status < B_OK) { gBufferModule->free(buffer); @@ -1205,35 +1181,3 @@ TCPConnection::_TimeWait(struct net_timer *timer, void *data) { } - -// #pragma mark - hash functions - - -int -TCPConnection::Compare(void *_connection, const void *_key) -{ - const tcp_connection_key *key = (tcp_connection_key *)_key; - TCPConnection *connection = ((TCPConnection *)_connection); - - if (gAddressModule->equal_addresses_and_ports(key->local, - (sockaddr *)&connection->socket->address) - && gAddressModule->equal_addresses_and_ports(key->peer, - (sockaddr *)&connection->socket->peer)) - return 0; - - return 1; -} - - -uint32 -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; - } - - const tcp_connection_key *key = (tcp_connection_key *)_key; - 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 7501d8840e..8ded5b78de 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h +++ b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h @@ -20,6 +20,8 @@ #include +class EndpointManager; + class TCPConnection : public net_protocol { public: TCPConnection(net_socket *socket); @@ -44,6 +46,7 @@ class TCPConnection : public net_protocol { size_t ReadAvailable(); tcp_state State() const { return fState; } + bool IsBound() const; status_t DelayedAcknowledge(); status_t SendAcknowledge(); @@ -53,11 +56,9 @@ class TCPConnection : public net_protocol { net_buffer *buffer); int32 Receive(tcp_segment_header& segment, net_buffer *buffer); - static int Compare(void *_packet, const void *_key); - static uint32 Hash(void *_packet, const void *_key, uint32 range); - static int32 HashOffset() { return offsetof(TCPConnection, fHashNext); } - private: + friend class EndpointManager; + void _StartPersistTimer(); uint8 _CurrentFlags(); bool _ShouldSendSegment(tcp_segment_header &segment, uint32 length, @@ -69,7 +70,9 @@ class TCPConnection : public net_protocol { static void _PersistTimer(net_timer *timer, void *data); static void _DelayedAcknowledgeTimer(net_timer *timer, void *data); - TCPConnection *fHashNext; + TCPConnection *fConnectionHashNext; + TCPConnection *fEndpointHashNext; + TCPConnection *fEndpointNextWithSamePort; recursive_lock fLock; sem_id fReceiveLock; diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index 0d9557be71..94c1cda899 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -8,6 +8,7 @@ */ +#include "EndpointManager.h" #include "TCPConnection.h" #include @@ -37,45 +38,13 @@ #endif -#define MAX_HASH_TCP 64 - - 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; -recursive_lock gConnectionLock; - - -#ifdef TRACE_TCP -# define DUMP_TCP_HASH tcp_dump_hash() -// Dumps the TCP Connection hash. gConnectionLock must NOT be held when calling -void -tcp_dump_hash() -{ - RecursiveLocker lock(&gConnectionLock); - if (gDomain == NULL) { - TRACE(("Unable to dump TCP Connections!\n")); - return; - } - struct hash_iterator iterator; - hash_open(gConnectionHash, &iterator); - TCPConnection *connection; - hash_rewind(gConnectionHash, &iterator); - TRACE(("Active TCP Connections:\n")); - while ((connection = (TCPConnection *)hash_next(gConnectionHash, &iterator)) != NULL) { - 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); -} -#else -# define DUMP_TCP_HASH ; -#endif +EndpointManager *gEndpointManager; status_t @@ -290,76 +259,6 @@ reply_with_reset(tcp_segment_header &segment, net_buffer *buffer) } -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 @@ -417,7 +316,6 @@ tcp_free(net_protocol *protocol) status_t tcp_connect(net_protocol *protocol, const struct sockaddr *address) { - DUMP_TCP_HASH; return ((TCPConnection *)protocol)->Connect(address); } @@ -569,11 +467,11 @@ tcp_receive_data(net_buffer *buffer) bufferHeader.Remove(headerLength); // we no longer need to keep the header around - RecursiveLocker hashLock(&gConnectionLock); + RecursiveLocker locker(gEndpointManager->Locker()); int32 segmentAction = DROP; - TCPConnection *connection = find_connection((struct sockaddr *)&buffer->destination, - (struct sockaddr *)&buffer->source); + TCPConnection *connection = gEndpointManager->FindConnection( + (struct sockaddr *)&buffer->destination, (struct sockaddr *)&buffer->source); if (connection != NULL) { RecursiveLocker locker(connection->Lock()); @@ -661,12 +559,12 @@ tcp_init() if (status < B_OK) goto err3; - gConnectionHash = hash_init(MAX_HASH_TCP, TCPConnection::HashOffset(), - &TCPConnection::Compare, &TCPConnection::Hash); - if (gConnectionHash == NULL) + gEndpointManager = new (std::nothrow) EndpointManager(); + if (gEndpointManager == NULL) { + status = B_NO_MEMORY; goto err4; - - status = recursive_lock_init(&gConnectionLock, "tcp connection hash"); + } + status = gEndpointManager->InitCheck(); if (status < B_OK) goto err5; @@ -675,26 +573,24 @@ tcp_init() "network/protocols/ipv4/v1", NULL); if (status < B_OK) - goto err6; + goto err5; 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 err6; + goto err5; status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP, "network/protocols/tcp/v1"); if (status < B_OK) - goto err6; + goto err5; return B_OK; -err6: - recursive_lock_destroy(&gConnectionLock); err5: - hash_uninit(gConnectionHash); + delete gEndpointManager; err4: put_module(NET_DATALINK_MODULE_NAME); err3: @@ -712,8 +608,8 @@ err1: static status_t tcp_uninit() { - recursive_lock_destroy(&gConnectionLock); - hash_uninit(gConnectionHash); + delete gEndpointManager; + put_module(NET_DATALINK_MODULE_NAME); put_module(NET_SOCKET_MODULE_NAME); put_module(NET_BUFFER_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 26b2b6cfc9..650fff2148 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.h +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.h @@ -21,7 +21,7 @@ #include -class TCPConnection; +class EndpointManager; typedef enum { // establishing a connection @@ -154,11 +154,6 @@ enum tcp_segment_action { IMMEDIATE_ACKNOWLEDGE = 0x08, }; -struct tcp_connection_key { - const sockaddr *local; - const sockaddr *peer; -}; - extern net_domain *gDomain; extern net_address_module_info *gAddressModule; @@ -166,12 +161,9 @@ 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 EndpointManager *gEndpointManager; status_t add_tcp_header(tcp_segment_header &segment, net_buffer *buffer); -status_t remove_connection(TCPConnection *connection); -status_t insert_connection(TCPConnection *connection); #endif // TCP_H diff --git a/src/tests/kits/net/Jamfile b/src/tests/kits/net/Jamfile index 518eda6dd3..eb170f3d3b 100644 --- a/src/tests/kits/net/Jamfile +++ b/src/tests/kits/net/Jamfile @@ -36,6 +36,7 @@ SimpleTest tcp_tester : tcp.cpp TCPConnection.cpp BufferQueue.cpp + EndpointManager.cpp # misc argv.c @@ -45,7 +46,7 @@ SimpleTest tcp_tester : ; SEARCH on [ FGristFiles - tcp.cpp TCPConnection.cpp BufferQueue.cpp + tcp.cpp TCPConnection.cpp BufferQueue.cpp EndpointManager.cpp ] = [ FDirName $(HAIKU_TOP) src add-ons kernel network protocols tcp ] ; SEARCH on [ FGristFiles