partially rewrote TCP's endpoint manager. Fixes #1173
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20814 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
#ifndef _OPEN_HASH_TABLE_H_
|
#ifndef _OPEN_HASH_TABLE_H_
|
||||||
#define _OPEN_HASH_TABLE_H_
|
#define _OPEN_HASH_TABLE_H_
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <KernelExport.h>
|
||||||
|
|
||||||
// the Definition template must have three methods: `HashKey', `Hash' and
|
// the Definition template must have three methods: `HashKey', `Hash' and
|
||||||
// `Compare'. It must also define several types as shown in the following
|
// `Compare'. It must also define several types as shown in the following
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
// is the same (property of the hash function) while not wasting one additional
|
// is the same (property of the hash function) while not wasting one additional
|
||||||
// word per item and having better cache locality. The usage of quadratic
|
// word per item and having better cache locality. The usage of quadratic
|
||||||
// probing reduces the effectiveness of cache locality but prevents clustering.
|
// probing reduces the effectiveness of cache locality but prevents clustering.
|
||||||
template<typename Definition>
|
template<typename Definition, bool CheckDuplicates = false>
|
||||||
class OpenHashTable {
|
class OpenHashTable {
|
||||||
public:
|
public:
|
||||||
typedef typename Definition::ParentType ParentType;
|
typedef typename Definition::ParentType ParentType;
|
||||||
@@ -100,6 +100,13 @@ public:
|
|||||||
|
|
||||||
void InsertUnchecked(ValueType *value)
|
void InsertUnchecked(ValueType *value)
|
||||||
{
|
{
|
||||||
|
if (CheckDuplicates) {
|
||||||
|
for (size_t i = 0; i < fTableSize; i++) {
|
||||||
|
if (fTable[i] == value)
|
||||||
|
panic("HashTable: item already in table");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ValueType *previous = _Insert(fTable, fTableSize, value);
|
ValueType *previous = _Insert(fTable, fTableSize, value);
|
||||||
if (_IsDeleted(previous))
|
if (_IsDeleted(previous))
|
||||||
fDeletedCount--;
|
fDeletedCount--;
|
||||||
@@ -128,6 +135,13 @@ public:
|
|||||||
index = _NextSlot(f, index, fTableSize);
|
index = _NextSlot(f, index, fTableSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CheckDuplicates) {
|
||||||
|
for (size_t i = 0; i < fTableSize; i++) {
|
||||||
|
if (fTable[i] == value)
|
||||||
|
panic("HashTable: item removed, but still in table.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fItemCount--;
|
fItemCount--;
|
||||||
fDeletedCount++;
|
fDeletedCount++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct net_protocol_module_info {
|
|||||||
status_t (*setsockopt)(net_protocol *self, int level, int option,
|
status_t (*setsockopt)(net_protocol *self, int level, int option,
|
||||||
const void *value, int length);
|
const void *value, int length);
|
||||||
|
|
||||||
status_t (*bind)(net_protocol *self, struct sockaddr *address);
|
status_t (*bind)(net_protocol *self, const struct sockaddr *address);
|
||||||
status_t (*unbind)(net_protocol *self, struct sockaddr *address);
|
status_t (*unbind)(net_protocol *self, struct sockaddr *address);
|
||||||
status_t (*listen)(net_protocol *self, int count);
|
status_t (*listen)(net_protocol *self, int count);
|
||||||
status_t (*shutdown)(net_protocol *self, int direction);
|
status_t (*shutdown)(net_protocol *self, int direction);
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ icmp_control(net_protocol *protocol, int level, int option, void *value,
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
icmp_bind(net_protocol *protocol, struct sockaddr *address)
|
icmp_bind(net_protocol *protocol, const struct sockaddr *address)
|
||||||
{
|
{
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1231,7 +1231,7 @@ ipv4_setsockopt(net_protocol *_protocol, int level, int option,
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
ipv4_bind(net_protocol *protocol, struct sockaddr *address)
|
ipv4_bind(net_protocol *protocol, const struct sockaddr *address)
|
||||||
{
|
{
|
||||||
if (address->sa_family != AF_INET)
|
if (address->sa_family != AF_INET)
|
||||||
return EAFNOSUPPORT;
|
return EAFNOSUPPORT;
|
||||||
|
|||||||
@@ -25,53 +25,79 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
struct connection_key {
|
|
||||||
net_address_module_info *address_module;
|
|
||||||
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 kLastReservedPort = 1023;
|
||||||
static const uint16 kFirstEphemeralPort = 40000;
|
static const uint16 kFirstEphemeralPort = 40000;
|
||||||
|
|
||||||
|
|
||||||
EndpointManager::EndpointManager(net_domain *domain)
|
size_t
|
||||||
: fDomain(domain)
|
ConnectionHashDefinition::HashKey(EndpointManager *manager, const KeyType &key)
|
||||||
{
|
{
|
||||||
fConnectionHash = hash_init(kConnectionHashBuckets,
|
return manager->AddressModule()->hash_address_pair(key.first, key.second);
|
||||||
offsetof(TCPEndpoint, fConnectionHashNext),
|
}
|
||||||
&_ConnectionCompare, &_ConnectionHash);
|
|
||||||
fEndpointHash = hash_init(kEndpointHashBuckets,
|
|
||||||
offsetof(TCPEndpoint, fEndpointHashNext),
|
|
||||||
&_EndpointCompare, &_EndpointHash);
|
|
||||||
|
|
||||||
recursive_lock_init(&fLock, "endpoint manager");
|
|
||||||
|
size_t
|
||||||
|
ConnectionHashDefinition::Hash(EndpointManager *manager, TCPEndpoint *endpoint)
|
||||||
|
{
|
||||||
|
return manager->AddressModule()->hash_address_pair(
|
||||||
|
endpoint->LocalAddress(), endpoint->PeerAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
ConnectionHashDefinition::Compare(EndpointManager *manager, const KeyType &key,
|
||||||
|
TCPEndpoint *endpoint)
|
||||||
|
{
|
||||||
|
net_address_module_info *module = manager->AddressModule();
|
||||||
|
|
||||||
|
return module->equal_addresses_and_ports(key.first, endpoint->LocalAddress())
|
||||||
|
&& module->equal_addresses_and_ports(key.second, endpoint->PeerAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t
|
||||||
|
EndpointHashDefinition::HashKey(EndpointManager *manager, uint16 port)
|
||||||
|
{
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t
|
||||||
|
EndpointHashDefinition::Hash(EndpointManager *manager, TCPEndpoint *endpoint)
|
||||||
|
{
|
||||||
|
return endpoint->AddressModule()->get_port(endpoint->LocalAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
EndpointHashDefinition::Compare(EndpointManager *manager, uint16 port,
|
||||||
|
TCPEndpoint *endpoint)
|
||||||
|
{
|
||||||
|
return endpoint->AddressModule()->get_port(endpoint->LocalAddress()) == port;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EndpointManager::EndpointManager(net_domain *domain)
|
||||||
|
: fDomain(domain), fConnectionHash(this), fEndpointHash(this)
|
||||||
|
{
|
||||||
|
benaphore_init(&fLock, "endpoint manager");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EndpointManager::~EndpointManager()
|
EndpointManager::~EndpointManager()
|
||||||
{
|
{
|
||||||
hash_uninit(fConnectionHash);
|
benaphore_destroy(&fLock);
|
||||||
hash_uninit(fEndpointHash);
|
|
||||||
|
|
||||||
recursive_lock_destroy(&fLock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
EndpointManager::InitCheck() const
|
EndpointManager::InitCheck() const
|
||||||
{
|
{
|
||||||
if (fConnectionHash == NULL
|
if (fConnectionHash.InitCheck() < B_OK)
|
||||||
|| fEndpointHash == NULL)
|
return fConnectionHash.InitCheck();
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
if (fEndpointHash.InitCheck() < B_OK)
|
||||||
|
return fEndpointHash.InitCheck();
|
||||||
|
|
||||||
if (fLock.sem < B_OK)
|
if (fLock.sem < B_OK)
|
||||||
return fLock.sem;
|
return fLock.sem;
|
||||||
@@ -88,43 +114,9 @@ EndpointManager::InitCheck() const
|
|||||||
You must hold the manager's lock when calling this method.
|
You must hold the manager's lock when calling this method.
|
||||||
*/
|
*/
|
||||||
TCPEndpoint *
|
TCPEndpoint *
|
||||||
EndpointManager::_LookupConnection(sockaddr *local, sockaddr *peer)
|
EndpointManager::_LookupConnection(const sockaddr *local, const sockaddr *peer)
|
||||||
{
|
{
|
||||||
connection_key key;
|
return fConnectionHash.Lookup(std::make_pair(local, peer));
|
||||||
key.address_module = AddressModule();
|
|
||||||
key.local = local;
|
|
||||||
key.peer = peer;
|
|
||||||
|
|
||||||
return (TCPEndpoint *)hash_lookup(fConnectionHash, &key);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
EndpointManager::_RemoveConnection(TCPEndpoint *endpoint)
|
|
||||||
{
|
|
||||||
RecursiveLocker locker(&fLock);
|
|
||||||
return hash_remove(fConnectionHash, endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
EndpointManager::_DumpConnections()
|
|
||||||
{
|
|
||||||
RecursiveLocker lock(&fLock);
|
|
||||||
|
|
||||||
struct hash_iterator iterator;
|
|
||||||
hash_open(fConnectionHash, &iterator);
|
|
||||||
|
|
||||||
TRACE(("Active TCP Connections:\n"));
|
|
||||||
|
|
||||||
TCPEndpoint *endpoint;
|
|
||||||
while ((endpoint = (TCPEndpoint *)hash_next(fConnectionHash, &iterator)) != NULL) {
|
|
||||||
TRACE((" TCPEndpoint %p: local %s, peer %s\n", endpoint,
|
|
||||||
AddressString(Domain(), (sockaddr *)&endpoint->socket->address, true).Data(),
|
|
||||||
AddressString(Domain(), (sockaddr *)&endpoint->socket->peer, true).Data()));
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_close(fConnectionHash, &iterator, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -134,7 +126,7 @@ EndpointManager::SetConnection(TCPEndpoint *endpoint,
|
|||||||
{
|
{
|
||||||
TRACE(("EndpointManager::SetConnection(%p)\n", endpoint));
|
TRACE(("EndpointManager::SetConnection(%p)\n", endpoint));
|
||||||
|
|
||||||
RecursiveLocker locker(&fLock);
|
BenaphoreLocker _(fLock);
|
||||||
sockaddr localBuffer;
|
sockaddr localBuffer;
|
||||||
|
|
||||||
// need to associate this connection with a real address, not INADDR_ANY
|
// need to associate this connection with a real address, not INADDR_ANY
|
||||||
@@ -144,26 +136,54 @@ EndpointManager::SetConnection(TCPEndpoint *endpoint,
|
|||||||
local = &localBuffer;
|
local = &localBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
connection_key key;
|
if (_LookupConnection(local, peer) != NULL)
|
||||||
key.address_module = AddressModule();
|
|
||||||
key.local = local;
|
|
||||||
key.peer = peer;
|
|
||||||
|
|
||||||
if (hash_lookup(fConnectionHash, &key) != NULL)
|
|
||||||
return EADDRINUSE;
|
return EADDRINUSE;
|
||||||
|
|
||||||
_RemoveConnection(endpoint);
|
AddressModule()->set_to(endpoint->LocalAddress(), local);
|
||||||
|
AddressModule()->set_to(endpoint->PeerAddress(), peer);
|
||||||
|
|
||||||
AddressModule()->set_to((sockaddr *)&endpoint->socket->address, local);
|
if (!fConnectionHash.Insert(endpoint))
|
||||||
AddressModule()->set_to((sockaddr *)&endpoint->socket->peer, peer);
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
return hash_insert(fConnectionHash, endpoint);
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
EndpointManager::SetPassive(TCPEndpoint *endpoint)
|
||||||
|
{
|
||||||
|
BenaphoreLocker _(fLock);
|
||||||
|
|
||||||
|
if (!endpoint->IsBound()) {
|
||||||
|
// if the socket is unbound first bind it to ephemeral
|
||||||
|
sockaddr_storage localAddress;
|
||||||
|
AddressModule()->set_to_empty_address((sockaddr *)&localAddress);
|
||||||
|
|
||||||
|
status_t status = _BindToEphemeral(endpoint,
|
||||||
|
(sockaddr *)&localAddress);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr_storage passive;
|
||||||
|
AddressModule()->set_to_empty_address((sockaddr *)&passive);
|
||||||
|
|
||||||
|
if (_LookupConnection(endpoint->LocalAddress(), (sockaddr *)&passive))
|
||||||
|
return EADDRINUSE;
|
||||||
|
|
||||||
|
AddressModule()->set_to(endpoint->PeerAddress(), (sockaddr *)&passive);
|
||||||
|
if (!fConnectionHash.Insert(endpoint))
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TCPEndpoint *
|
TCPEndpoint *
|
||||||
EndpointManager::FindConnection(sockaddr *local, sockaddr *peer)
|
EndpointManager::FindConnection(sockaddr *local, sockaddr *peer)
|
||||||
{
|
{
|
||||||
|
BenaphoreLocker _(fLock);
|
||||||
|
|
||||||
TCPEndpoint *endpoint = _LookupConnection(local, peer);
|
TCPEndpoint *endpoint = _LookupConnection(local, peer);
|
||||||
if (endpoint != NULL) {
|
if (endpoint != NULL) {
|
||||||
TRACE(("TCP: Received packet corresponds to explicit endpoint %p\n", endpoint));
|
TRACE(("TCP: Received packet corresponds to explicit endpoint %p\n", endpoint));
|
||||||
@@ -193,7 +213,6 @@ EndpointManager::FindConnection(sockaddr *local, sockaddr *peer)
|
|||||||
|
|
||||||
// no matching endpoint exists
|
// no matching endpoint exists
|
||||||
TRACE(("TCP: no matching endpoint!\n"));
|
TRACE(("TCP: no matching endpoint!\n"));
|
||||||
_DumpConnections();
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -202,81 +221,45 @@ EndpointManager::FindConnection(sockaddr *local, sockaddr *peer)
|
|||||||
// #pragma mark - endpoints
|
// #pragma mark - endpoints
|
||||||
|
|
||||||
|
|
||||||
TCPEndpoint *
|
status_t
|
||||||
EndpointManager::_LookupEndpoint(uint16 port)
|
EndpointManager::Bind(TCPEndpoint *endpoint, const sockaddr *address)
|
||||||
{
|
{
|
||||||
endpoint_key key;
|
// TODO check the family:
|
||||||
key.port = port;
|
//
|
||||||
|
// if (!AddressModule()->is_understandable(address))
|
||||||
|
// return EAFNOSUPPORT;
|
||||||
|
|
||||||
return (TCPEndpoint *)hash_lookup(fEndpointHash, &key);
|
BenaphoreLocker _(fLock);
|
||||||
|
|
||||||
|
if (AddressModule()->get_port(address) == 0)
|
||||||
|
return _BindToEphemeral(endpoint, address);
|
||||||
|
|
||||||
|
return _BindToAddress(endpoint, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
EndpointManager::Bind(TCPEndpoint *endpoint)
|
EndpointManager::_BindToAddress(TCPEndpoint *endpoint, const sockaddr *address)
|
||||||
{
|
{
|
||||||
sockaddr *address = (sockaddr *)&endpoint->socket->address;
|
TRACE(("EndpointManager::BindToAddress(%p)\n", endpoint));
|
||||||
|
|
||||||
TRACE(("EndpointManager::Bind(%p, %s)\n", endpoint,
|
|
||||||
AddressString(Domain(), address, true).Data()));
|
|
||||||
|
|
||||||
if (AddressModule()->is_empty_address(address, true))
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
uint16 port = AddressModule()->get_port(address);
|
uint16 port = AddressModule()->get_port(address);
|
||||||
|
|
||||||
// TODO: check the root group instead?
|
// TODO this check follows very typical UNIX semantics
|
||||||
|
// and generally should be improved.
|
||||||
if (ntohs(port) <= kLastReservedPort && geteuid() != 0)
|
if (ntohs(port) <= kLastReservedPort && geteuid() != 0)
|
||||||
return B_PERMISSION_DENIED;
|
return B_PERMISSION_DENIED;
|
||||||
|
|
||||||
RecursiveLocker locker(&fLock);
|
return _Bind(endpoint, address);
|
||||||
|
|
||||||
TCPEndpoint *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) {
|
|
||||||
TCPEndpoint *last = first;
|
|
||||||
while (true) {
|
|
||||||
// check if this endpoint binds to a wildcard address
|
|
||||||
if (AddressModule()->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
|
status_t
|
||||||
EndpointManager::BindToEphemeral(TCPEndpoint *endpoint)
|
EndpointManager::_BindToEphemeral(TCPEndpoint *endpoint,
|
||||||
|
const sockaddr *address)
|
||||||
{
|
{
|
||||||
TRACE(("EndpointManager::BindToEphemeral(%p)\n", endpoint));
|
TRACE(("EndpointManager::BindToEphemeral(%p)\n", endpoint));
|
||||||
|
|
||||||
RecursiveLocker locker(&fLock);
|
|
||||||
|
|
||||||
uint32 max = kFirstEphemeralPort + 65536;
|
uint32 max = kFirstEphemeralPort + 65536;
|
||||||
|
|
||||||
for (int32 i = 1; i < 5; i++) {
|
for (int32 i = 1; i < 5; i++) {
|
||||||
@@ -291,16 +274,17 @@ EndpointManager::BindToEphemeral(TCPEndpoint *endpoint)
|
|||||||
|
|
||||||
port = htons(port);
|
port = htons(port);
|
||||||
|
|
||||||
TCPEndpoint *other = _LookupEndpoint(port);
|
TCPEndpoint *other = fEndpointHash.Lookup(port);
|
||||||
if (other == NULL) {
|
if (other == NULL) {
|
||||||
|
sockaddr_storage newAddress;
|
||||||
|
AddressModule()->set_to((sockaddr *)&newAddress, address);
|
||||||
|
AddressModule()->set_port((sockaddr *)&newAddress, port);
|
||||||
|
|
||||||
// found a port
|
// found a port
|
||||||
AddressModule()->set_port((sockaddr *)&endpoint->socket->address, port);
|
|
||||||
TRACE((" EndpointManager::BindToEphemeral(%p) -> %s\n", endpoint,
|
TRACE((" EndpointManager::BindToEphemeral(%p) -> %s\n", endpoint,
|
||||||
AddressString(Domain(), (sockaddr *)&endpoint->socket->address, true).Data()));
|
AddressString(Domain(), (sockaddr *)&newAddress, true).Data()));
|
||||||
endpoint->fEndpointNextWithSamePort = NULL;
|
|
||||||
hash_insert(fEndpointHash, endpoint);
|
return _Bind(endpoint, (sockaddr *)&newAddress);
|
||||||
hash_insert(fConnectionHash, endpoint);
|
|
||||||
return B_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
counter += step;
|
counter += step;
|
||||||
@@ -313,38 +297,92 @@ EndpointManager::BindToEphemeral(TCPEndpoint *endpoint)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
EndpointManager::Unbind(TCPEndpoint *endpoint)
|
EndpointManager::_Bind(TCPEndpoint *endpoint, const sockaddr *address)
|
||||||
{
|
{
|
||||||
if (endpoint == NULL || !endpoint->IsBound())
|
uint16 port = AddressModule()->get_port(address);
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
RecursiveLocker locker(&fLock);
|
TCPEndpoint *first = fEndpointHash.Lookup(port);
|
||||||
|
|
||||||
if (!endpoint->fSpawned) {
|
// If there is already an endpoint bound to that port, SO_REUSEADDR has to be
|
||||||
TCPEndpoint *other = _LookupEndpoint(AddressModule()->get_port(
|
// specified by the new endpoint to be allowed to bind to that same port.
|
||||||
(sockaddr *)&endpoint->socket->address));
|
// Alternatively, all endpoints must have the SO_REUSEPORT option set.
|
||||||
if (other != endpoint) {
|
if (first != NULL
|
||||||
// remove endpoint from the list of endpoints with the same port
|
&& (endpoint->socket->options & SO_REUSEADDR) == 0
|
||||||
while (other != NULL && other->fEndpointNextWithSamePort != endpoint) {
|
&& ((endpoint->socket->options & SO_REUSEPORT) == 0
|
||||||
other = other->fEndpointNextWithSamePort;
|
|| (first->socket->options & SO_REUSEPORT) == 0))
|
||||||
|
return EADDRINUSE;
|
||||||
|
|
||||||
|
TCPEndpoint *insertionPoint = NULL;
|
||||||
|
|
||||||
|
if (first != NULL) {
|
||||||
|
while (true) {
|
||||||
|
// check if this endpoint binds to a wildcard address
|
||||||
|
if (AddressModule()->is_empty_address(first->LocalAddress(), false)) {
|
||||||
|
// you cannot specialize a wildcard endpoint - you have to open the
|
||||||
|
// wildcard endpoint last
|
||||||
|
return B_PERMISSION_DENIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (other != NULL)
|
if (first->fEndpointNextWithSamePort == NULL)
|
||||||
other->fEndpointNextWithSamePort = endpoint->fEndpointNextWithSamePort;
|
break;
|
||||||
else
|
|
||||||
panic("bound endpoint %p not in hash!", endpoint);
|
|
||||||
} else {
|
|
||||||
// we need to replace the first endpoint in the list
|
|
||||||
hash_remove(fEndpointHash, endpoint);
|
|
||||||
|
|
||||||
other = endpoint->fEndpointNextWithSamePort;
|
first = first->fEndpointNextWithSamePort;
|
||||||
if (other != NULL)
|
|
||||||
hash_insert(fEndpointHash, other);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insertionPoint = first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thus far we have checked if the Bind() is allowed
|
||||||
|
|
||||||
|
status_t status = endpoint->next->module->bind(endpoint->next, address);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
endpoint->fEndpointNextWithSamePort = NULL;
|
||||||
|
|
||||||
|
if (insertionPoint)
|
||||||
|
insertionPoint->fEndpointNextWithSamePort = endpoint;
|
||||||
|
else
|
||||||
|
fEndpointHash.Insert(endpoint);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
EndpointManager::Unbind(TCPEndpoint *endpoint)
|
||||||
|
{
|
||||||
|
TRACE(("EndpointManager::Unbind(%p)\n", endpoint));
|
||||||
|
|
||||||
|
if (endpoint == NULL || !endpoint->IsBound()) {
|
||||||
|
TRACE((" endpoint is unbound.\n"));
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BenaphoreLocker _(fLock);
|
||||||
|
|
||||||
|
TCPEndpoint *other = fEndpointHash.Lookup(
|
||||||
|
AddressModule()->get_port(endpoint->LocalAddress()));
|
||||||
|
if (other != endpoint) {
|
||||||
|
// remove endpoint from the list of endpoints with the same port
|
||||||
|
while (other != NULL && other->fEndpointNextWithSamePort != endpoint)
|
||||||
|
other = other->fEndpointNextWithSamePort;
|
||||||
|
|
||||||
|
if (other != NULL)
|
||||||
|
other->fEndpointNextWithSamePort = endpoint->fEndpointNextWithSamePort;
|
||||||
|
else if (!endpoint->fSpawned)
|
||||||
|
panic("bound endpoint %p not in hash!", endpoint);
|
||||||
|
} else {
|
||||||
|
// we need to replace the first endpoint in the list
|
||||||
|
fEndpointHash.Remove(endpoint);
|
||||||
|
|
||||||
|
other = endpoint->fEndpointNextWithSamePort;
|
||||||
|
if (other != NULL)
|
||||||
|
fEndpointHash.Insert(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoint->fEndpointNextWithSamePort = NULL;
|
endpoint->fEndpointNextWithSamePort = NULL;
|
||||||
_RemoveConnection(endpoint);
|
fConnectionHash.Remove(endpoint);
|
||||||
|
|
||||||
endpoint->socket->address.ss_len = 0;
|
endpoint->socket->address.ss_len = 0;
|
||||||
|
|
||||||
@@ -389,70 +427,3 @@ EndpointManager::ReplyWithReset(tcp_segment_header &segment,
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - hash functions
|
|
||||||
|
|
||||||
|
|
||||||
/*static*/ int
|
|
||||||
EndpointManager::_ConnectionCompare(void *_endpoint, const void *_key)
|
|
||||||
{
|
|
||||||
const connection_key *key = (connection_key *)_key;
|
|
||||||
TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint;
|
|
||||||
|
|
||||||
if (key->address_module->equal_addresses_and_ports(key->local,
|
|
||||||
(sockaddr *)&endpoint->socket->address)
|
|
||||||
&& key->address_module->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)
|
|
||||||
{
|
|
||||||
net_address_module_info *address_module;
|
|
||||||
const sockaddr *local;
|
|
||||||
const sockaddr *peer;
|
|
||||||
|
|
||||||
if (_endpoint != NULL) {
|
|
||||||
TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint;
|
|
||||||
address_module = endpoint->AddressModule();
|
|
||||||
local = (sockaddr *)&endpoint->socket->address;
|
|
||||||
peer = (sockaddr *)&endpoint->socket->peer;
|
|
||||||
} else {
|
|
||||||
const connection_key *key = (connection_key *)_key;
|
|
||||||
address_module = key->address_module;
|
|
||||||
local = key->local;
|
|
||||||
peer = key->peer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return address_module->hash_address_pair(local, peer) % range;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*static*/ int
|
|
||||||
EndpointManager::_EndpointCompare(void *_endpoint, const void *_key)
|
|
||||||
{
|
|
||||||
const endpoint_key *key = (endpoint_key *)_key;
|
|
||||||
TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint;
|
|
||||||
|
|
||||||
return endpoint->AddressModule()->get_port(
|
|
||||||
(sockaddr *)&endpoint->socket->address) == key->port ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*static*/ uint32
|
|
||||||
EndpointManager::_EndpointHash(void *_endpoint, const void *_key, uint32 range)
|
|
||||||
{
|
|
||||||
if (_endpoint != NULL) {
|
|
||||||
TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint;
|
|
||||||
return endpoint->AddressModule()->get_port(
|
|
||||||
(sockaddr *)&endpoint->socket->address) % range;
|
|
||||||
}
|
|
||||||
|
|
||||||
const endpoint_key *key = (endpoint_key *)_key;
|
|
||||||
return key->port % range;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,15 +14,42 @@
|
|||||||
|
|
||||||
#include <lock.h>
|
#include <lock.h>
|
||||||
#include <util/DoublyLinkedList.h>
|
#include <util/DoublyLinkedList.h>
|
||||||
#include <util/khash.h>
|
#include <util/OpenHashTable.h>
|
||||||
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
struct net_address_module_info;
|
struct net_address_module_info;
|
||||||
struct net_domain;
|
struct net_domain;
|
||||||
|
class EndpointManager;
|
||||||
class TCPEndpoint;
|
class TCPEndpoint;
|
||||||
|
|
||||||
|
struct ConnectionHashDefinition {
|
||||||
|
typedef EndpointManager *ParentType;
|
||||||
|
typedef std::pair<const sockaddr *, const sockaddr *> KeyType;
|
||||||
|
typedef TCPEndpoint ValueType;
|
||||||
|
|
||||||
|
static size_t HashKey(EndpointManager *manager, const KeyType &key);
|
||||||
|
static size_t Hash(EndpointManager *manager, TCPEndpoint *endpoint);
|
||||||
|
static bool Compare(EndpointManager *manager, const KeyType &key,
|
||||||
|
TCPEndpoint *endpoint);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct EndpointHashDefinition {
|
||||||
|
typedef EndpointManager *ParentType;
|
||||||
|
typedef uint16 KeyType;
|
||||||
|
typedef TCPEndpoint ValueType;
|
||||||
|
|
||||||
|
static size_t HashKey(EndpointManager *manager, uint16 port);
|
||||||
|
static size_t Hash(EndpointManager *manager, TCPEndpoint *endpoint);
|
||||||
|
static bool Compare(EndpointManager *manager, uint16 port,
|
||||||
|
TCPEndpoint *endpoint);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class EndpointManager : public DoublyLinkedListLinkImpl<EndpointManager> {
|
class EndpointManager : public DoublyLinkedListLinkImpl<EndpointManager> {
|
||||||
public:
|
public:
|
||||||
EndpointManager(net_domain *domain);
|
EndpointManager(net_domain *domain);
|
||||||
@@ -30,14 +57,13 @@ class EndpointManager : public DoublyLinkedListLinkImpl<EndpointManager> {
|
|||||||
|
|
||||||
status_t InitCheck() const;
|
status_t InitCheck() const;
|
||||||
|
|
||||||
recursive_lock *Locker() { return &fLock; }
|
TCPEndpoint *FindConnection(sockaddr *local, sockaddr *peer);
|
||||||
|
|
||||||
status_t SetConnection(TCPEndpoint *endpoint, const sockaddr *local,
|
status_t SetConnection(TCPEndpoint *endpoint, const sockaddr *local,
|
||||||
const sockaddr *peer, const sockaddr *interfaceLocal);
|
const sockaddr *peer, const sockaddr *interfaceLocal);
|
||||||
TCPEndpoint *FindConnection(sockaddr *local, sockaddr *peer);
|
status_t SetPassive(TCPEndpoint *endpoint);
|
||||||
|
|
||||||
status_t Bind(TCPEndpoint *endpoint);
|
status_t Bind(TCPEndpoint *endpoint, const sockaddr *address);
|
||||||
status_t BindToEphemeral(TCPEndpoint *endpoint);
|
|
||||||
status_t Unbind(TCPEndpoint *endpoint);
|
status_t Unbind(TCPEndpoint *endpoint);
|
||||||
|
|
||||||
status_t ReplyWithReset(tcp_segment_header &segment,
|
status_t ReplyWithReset(tcp_segment_header &segment,
|
||||||
@@ -48,21 +74,18 @@ class EndpointManager : public DoublyLinkedListLinkImpl<EndpointManager> {
|
|||||||
{ return Domain()->address_module; }
|
{ return Domain()->address_module; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TCPEndpoint *_LookupConnection(sockaddr *local, sockaddr *peer);
|
TCPEndpoint *_LookupConnection(const sockaddr *local,
|
||||||
status_t _RemoveConnection(TCPEndpoint *endpoint);
|
const sockaddr *peer);
|
||||||
TCPEndpoint *_LookupEndpoint(uint16 port);
|
status_t _Bind(TCPEndpoint *endpoint, const sockaddr *address);
|
||||||
void _DumpConnections();
|
status_t _BindToAddress(TCPEndpoint *endpoint, const sockaddr *address);
|
||||||
|
status_t _BindToEphemeral(TCPEndpoint *endpoint,
|
||||||
static int _ConnectionCompare(void *_endpoint, const void *_key);
|
const sockaddr *address);
|
||||||
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);
|
|
||||||
|
|
||||||
net_domain *fDomain;
|
net_domain *fDomain;
|
||||||
|
|
||||||
hash_table *fConnectionHash;
|
OpenHashTable<ConnectionHashDefinition> fConnectionHash;
|
||||||
hash_table *fEndpointHash;
|
OpenHashTable<EndpointHashDefinition> fEndpointHash;
|
||||||
recursive_lock fLock;
|
benaphore fLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ENDPOINT_MANAGER_H
|
#endif // ENDPOINT_MANAGER_H
|
||||||
|
|||||||
@@ -450,7 +450,7 @@ TCPEndpoint::Accept(struct net_socket **_acceptedSocket)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
TCPEndpoint::Bind(sockaddr *address)
|
TCPEndpoint::Bind(const sockaddr *address)
|
||||||
{
|
{
|
||||||
if (address == NULL)
|
if (address == NULL)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
@@ -462,20 +462,7 @@ TCPEndpoint::Bind(sockaddr *address)
|
|||||||
if (fState != CLOSED)
|
if (fState != CLOSED)
|
||||||
return EISCONN;
|
return EISCONN;
|
||||||
|
|
||||||
// let IP check whether there is an interface that supports the given address:
|
return fManager->Bind(this, address);
|
||||||
status_t status = next->module->bind(next, address);
|
|
||||||
if (status < B_OK)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
if (AddressModule()->get_port(address) == 0)
|
|
||||||
status = fManager->BindToEphemeral(this);
|
|
||||||
else
|
|
||||||
status = fManager->Bind(this);
|
|
||||||
|
|
||||||
TRACE(" Bind() bound to %s (status %i)", PrintAddress(&socket->address),
|
|
||||||
(int)status);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -498,13 +485,18 @@ TCPEndpoint::Listen(int count)
|
|||||||
|
|
||||||
if (fState != CLOSED)
|
if (fState != CLOSED)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
if (!IsBound())
|
|
||||||
return EDESTADDRREQ;
|
|
||||||
|
|
||||||
fAcceptSemaphore = create_sem(0, "tcp accept");
|
fAcceptSemaphore = create_sem(0, "tcp accept");
|
||||||
if (fAcceptSemaphore < B_OK)
|
if (fAcceptSemaphore < B_OK)
|
||||||
return ENOBUFS;
|
return ENOBUFS;
|
||||||
|
|
||||||
|
status_t status = fManager->SetPassive(this);
|
||||||
|
if (status < B_OK) {
|
||||||
|
delete_sem(fAcceptSemaphore);
|
||||||
|
fAcceptSemaphore = -1;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
fState = LISTEN;
|
fState = LISTEN;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class TCPEndpoint : public net_protocol {
|
|||||||
status_t Free();
|
status_t Free();
|
||||||
status_t Connect(const struct sockaddr *address);
|
status_t Connect(const struct sockaddr *address);
|
||||||
status_t Accept(struct net_socket **_acceptedSocket);
|
status_t Accept(struct net_socket **_acceptedSocket);
|
||||||
status_t Bind(struct sockaddr *address);
|
status_t Bind(const sockaddr *address);
|
||||||
status_t Unbind(struct sockaddr *address);
|
status_t Unbind(struct sockaddr *address);
|
||||||
status_t Listen(int count);
|
status_t Listen(int count);
|
||||||
status_t Shutdown(int direction);
|
status_t Shutdown(int direction);
|
||||||
@@ -65,6 +65,16 @@ class TCPEndpoint : public net_protocol {
|
|||||||
tcp_state State() const { return fState; }
|
tcp_state State() const { return fState; }
|
||||||
bool IsBound() const;
|
bool IsBound() const;
|
||||||
|
|
||||||
|
const sockaddr *LocalAddress() const
|
||||||
|
{ return (sockaddr *)&socket->address; }
|
||||||
|
sockaddr *LocalAddress()
|
||||||
|
{ return (sockaddr *)&socket->address; }
|
||||||
|
|
||||||
|
const sockaddr *PeerAddress() const
|
||||||
|
{ return (sockaddr *)&socket->peer; }
|
||||||
|
sockaddr *PeerAddress()
|
||||||
|
{ return (sockaddr *)&socket->peer; }
|
||||||
|
|
||||||
void DeleteSocket();
|
void DeleteSocket();
|
||||||
|
|
||||||
status_t DelayedAcknowledge();
|
status_t DelayedAcknowledge();
|
||||||
|
|||||||
@@ -500,7 +500,7 @@ tcp_setsockopt(net_protocol *_protocol, int level, int option,
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
tcp_bind(net_protocol *protocol, struct sockaddr *address)
|
tcp_bind(net_protocol *protocol, const struct sockaddr *address)
|
||||||
{
|
{
|
||||||
return ((TCPEndpoint *)protocol)->Bind(address);
|
return ((TCPEndpoint *)protocol)->Bind(address);
|
||||||
}
|
}
|
||||||
@@ -630,7 +630,6 @@ tcp_receive_data(net_buffer *buffer)
|
|||||||
if (endpointManager == NULL)
|
if (endpointManager == NULL)
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
RecursiveLocker locker(endpointManager->Locker());
|
|
||||||
int32 segmentAction = DROP;
|
int32 segmentAction = DROP;
|
||||||
|
|
||||||
TCPEndpoint *endpoint = endpointManager->FindConnection(
|
TCPEndpoint *endpoint = endpointManager->FindConnection(
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class UdpEndpoint : public net_protocol, public DatagramSocket<> {
|
|||||||
public:
|
public:
|
||||||
UdpEndpoint(net_socket *socket);
|
UdpEndpoint(net_socket *socket);
|
||||||
|
|
||||||
status_t Bind(sockaddr *newAddr);
|
status_t Bind(const sockaddr *newAddr);
|
||||||
status_t Unbind(sockaddr *newAddr);
|
status_t Unbind(sockaddr *newAddr);
|
||||||
status_t Connect(const sockaddr *newAddr);
|
status_t Connect(const sockaddr *newAddr);
|
||||||
|
|
||||||
@@ -711,7 +711,7 @@ UdpEndpoint::UdpEndpoint(net_socket *socket)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
UdpEndpoint::Bind(sockaddr *address)
|
UdpEndpoint::Bind(const sockaddr *address)
|
||||||
{
|
{
|
||||||
TRACE_EP("Bind(%s)", AddressString(Domain(), address, true).Data());
|
TRACE_EP("Bind(%s)", AddressString(Domain(), address, true).Data());
|
||||||
|
|
||||||
@@ -1021,7 +1021,7 @@ udp_control(net_protocol *protocol, int level, int option, void *value,
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
udp_bind(net_protocol *protocol, struct sockaddr *address)
|
udp_bind(net_protocol *protocol, const struct sockaddr *address)
|
||||||
{
|
{
|
||||||
return ((UdpEndpoint *)protocol)->Bind(address);
|
return ((UdpEndpoint *)protocol)->Bind(address);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ link_control(net_protocol *_protocol, int level, int option, void *value,
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
link_bind(net_protocol *protocol, struct sockaddr *address)
|
link_bind(net_protocol *protocol, const struct sockaddr *address)
|
||||||
{
|
{
|
||||||
// TODO: bind to a specific interface and ethernet type
|
// TODO: bind to a specific interface and ethernet type
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|||||||
Reference in New Issue
Block a user