Rewrote how bind() works:

* there are now two hash tables: one for connections, and one for ports
* the first one is used to find the endpoint for incoming connections
* the latter is used to check if the address to bind() to is still available
  (incl. support for SO_REUSEADDR, and SO_REUSEPORT). Specialising an existing
  socket is not allowed, though; wildcard sockets need to be started last.
* the TCPConnection class now has a pointer to the next endpoint with the
  same port number - this list is scanned for the existing bound sockets
  on that port.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19416 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-12-04 13:44:35 +00:00
parent f7d2a39699
commit 9cbe6ce200
8 changed files with 520 additions and 230 deletions

View File

@ -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 <NetUtilities.h>
#include <util/AutoLock.h>
#include <KernelExport.h>
#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;
}

View File

@ -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 <lock.h>
#include <util/khash.h>
#include <sys/socket.h>
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

View File

@ -16,6 +16,7 @@ KernelAddon tcp :
tcp.cpp
TCPConnection.cpp
BufferQueue.cpp
EndpointManager.cpp
;
# Installation

View File

@ -9,12 +9,19 @@
#include "TCPConnection.h"
#include "EndpointManager.h"
#include <net_buffer.h>
#include <net_datalink.h>
#include <NetBufferUtilities.h>
#include <NetUtilities.h>
#include <lock.h>
#include <util/AutoLock.h>
#include <util/khash.h>
#include <util/list.h>
#include <KernelExport.h>
#include <util/list.h>
#include <netinet/in.h>
#include <netinet/ip.h>
@ -23,13 +30,6 @@
#include <stdlib.h>
#include <string.h>
#include <lock.h>
#include <util/AutoLock.h>
#include <util/khash.h>
#include <NetBufferUtilities.h>
#include <NetUtilities.h>
// 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;
}

View File

@ -20,6 +20,8 @@
#include <stddef.h>
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;

View File

@ -8,6 +8,7 @@
*/
#include "EndpointManager.h"
#include "TCPConnection.h"
#include <net_protocol.h>
@ -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);

View File

@ -21,7 +21,7 @@
#include <sys/socket.h>
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

View File

@ -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