* No longer leaks the EndpointManagers at unload.
* Now uses an array instead of a doubly linked list to find the endpoint manager for a domain. * No longer locks the endpoint managers during TCP processing, which actually made all TCP input serialized. * Minor cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29210 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
|
* Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
|
|
||||||
#include <NetUtilities.h>
|
#include <NetUtilities.h>
|
||||||
#include <util/AutoLock.h>
|
|
||||||
#include <tracing.h>
|
#include <tracing.h>
|
||||||
|
|
||||||
#include "TCPEndpoint.h"
|
#include "TCPEndpoint.h"
|
||||||
@@ -221,13 +220,13 @@ EndpointManager::EndpointManager(net_domain* domain)
|
|||||||
fConnectionHash(this),
|
fConnectionHash(this),
|
||||||
fLastPort(kFirstEphemeralPort)
|
fLastPort(kFirstEphemeralPort)
|
||||||
{
|
{
|
||||||
mutex_init(&fLock, "endpoint manager");
|
rw_lock_init(&fLock, "TCP endpoint manager");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EndpointManager::~EndpointManager()
|
EndpointManager::~EndpointManager()
|
||||||
{
|
{
|
||||||
mutex_destroy(&fLock);
|
rw_lock_destroy(&fLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -246,7 +245,8 @@ EndpointManager::Init()
|
|||||||
|
|
||||||
|
|
||||||
/*! Returns the endpoint matching the connection.
|
/*! Returns the endpoint matching the connection.
|
||||||
You must hold the manager's lock when calling this method.
|
You must hold the manager's lock when calling this method (either read or
|
||||||
|
write).
|
||||||
*/
|
*/
|
||||||
TCPEndpoint*
|
TCPEndpoint*
|
||||||
EndpointManager::_LookupConnection(const sockaddr* local, const sockaddr* peer)
|
EndpointManager::_LookupConnection(const sockaddr* local, const sockaddr* peer)
|
||||||
@@ -261,7 +261,7 @@ EndpointManager::SetConnection(TCPEndpoint* endpoint, const sockaddr* _local,
|
|||||||
{
|
{
|
||||||
TRACE(("EndpointManager::SetConnection(%p)\n", endpoint));
|
TRACE(("EndpointManager::SetConnection(%p)\n", endpoint));
|
||||||
|
|
||||||
MutexLocker _(fLock);
|
WriteLocker _(fLock);
|
||||||
|
|
||||||
SocketAddressStorage local(AddressModule());
|
SocketAddressStorage local(AddressModule());
|
||||||
local.SetTo(_local);
|
local.SetTo(_local);
|
||||||
@@ -287,7 +287,7 @@ EndpointManager::SetConnection(TCPEndpoint* endpoint, const sockaddr* _local,
|
|||||||
status_t
|
status_t
|
||||||
EndpointManager::SetPassive(TCPEndpoint* endpoint)
|
EndpointManager::SetPassive(TCPEndpoint* endpoint)
|
||||||
{
|
{
|
||||||
MutexLocker _(fLock);
|
WriteLocker _(fLock);
|
||||||
|
|
||||||
if (!endpoint->IsBound()) {
|
if (!endpoint->IsBound()) {
|
||||||
// if the socket is unbound first bind it to ephemeral
|
// if the socket is unbound first bind it to ephemeral
|
||||||
@@ -314,7 +314,7 @@ EndpointManager::SetPassive(TCPEndpoint* endpoint)
|
|||||||
TCPEndpoint*
|
TCPEndpoint*
|
||||||
EndpointManager::FindConnection(sockaddr* local, sockaddr* peer)
|
EndpointManager::FindConnection(sockaddr* local, sockaddr* peer)
|
||||||
{
|
{
|
||||||
MutexLocker _(fLock);
|
ReadLocker _(fLock);
|
||||||
|
|
||||||
TCPEndpoint *endpoint = _LookupConnection(local, peer);
|
TCPEndpoint *endpoint = _LookupConnection(local, peer);
|
||||||
if (endpoint != NULL) {
|
if (endpoint != NULL) {
|
||||||
@@ -356,31 +356,31 @@ EndpointManager::FindConnection(sockaddr* local, sockaddr* peer)
|
|||||||
status_t
|
status_t
|
||||||
EndpointManager::Bind(TCPEndpoint* endpoint, const sockaddr* address)
|
EndpointManager::Bind(TCPEndpoint* endpoint, const sockaddr* address)
|
||||||
{
|
{
|
||||||
// TODO check the family:
|
// TODO: check the family:
|
||||||
//
|
|
||||||
// if (!AddressModule()->is_understandable(address))
|
// if (!AddressModule()->is_understandable(address))
|
||||||
// return EAFNOSUPPORT;
|
// return EAFNOSUPPORT;
|
||||||
|
|
||||||
MutexLocker _(fLock);
|
WriteLocker locker(fLock);
|
||||||
|
|
||||||
if (AddressModule()->get_port(address) == 0)
|
if (AddressModule()->get_port(address) == 0)
|
||||||
return _BindToEphemeral(endpoint, address);
|
return _BindToEphemeral(endpoint, address);
|
||||||
|
|
||||||
return _BindToAddress(endpoint, address);
|
return _BindToAddress(locker, endpoint, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
EndpointManager::BindChild(TCPEndpoint* endpoint)
|
EndpointManager::BindChild(TCPEndpoint* endpoint)
|
||||||
{
|
{
|
||||||
MutexLocker _(fLock);
|
WriteLocker _(fLock);
|
||||||
return _Bind(endpoint, *endpoint->LocalAddress());
|
return _Bind(endpoint, *endpoint->LocalAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! You must hold fLock when calling this method. */
|
/*! You must have fLock write locked when calling this method. */
|
||||||
status_t
|
status_t
|
||||||
EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address)
|
EndpointManager::_BindToAddress(WriteLocker& locker, TCPEndpoint* endpoint,
|
||||||
|
const sockaddr* _address)
|
||||||
{
|
{
|
||||||
ConstSocketAddress address(AddressModule(), _address);
|
ConstSocketAddress address(AddressModule(), _address);
|
||||||
uint16 port = address.Port();
|
uint16 port = address.Port();
|
||||||
@@ -388,7 +388,7 @@ EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address)
|
|||||||
TRACE(("EndpointManager::BindToAddress(%p)\n", endpoint));
|
TRACE(("EndpointManager::BindToAddress(%p)\n", endpoint));
|
||||||
T(Bind(endpoint, address, false));
|
T(Bind(endpoint, address, false));
|
||||||
|
|
||||||
// TODO this check follows very typical UNIX semantics
|
// TODO: this check follows very typical UNIX semantics
|
||||||
// and generally should be improved.
|
// 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;
|
||||||
@@ -414,9 +414,9 @@ EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address)
|
|||||||
&& (userState > ESTABLISHED || userState == CLOSED)) {
|
&& (userState > ESTABLISHED || userState == CLOSED)) {
|
||||||
// This is a closing local connection - wait until it's
|
// This is a closing local connection - wait until it's
|
||||||
// gone away for real
|
// gone away for real
|
||||||
mutex_unlock(&fLock);
|
locker.Unlock();
|
||||||
snooze(10000);
|
snooze(10000);
|
||||||
mutex_lock(&fLock);
|
locker.Lock();
|
||||||
// TODO: make this better
|
// TODO: make this better
|
||||||
if (!retrying) {
|
if (!retrying) {
|
||||||
retrying = true;
|
retrying = true;
|
||||||
@@ -438,7 +438,7 @@ EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! You must hold fLock when calling this method. */
|
/*! You must have fLock write locked when calling this method. */
|
||||||
status_t
|
status_t
|
||||||
EndpointManager::_BindToEphemeral(TCPEndpoint* endpoint,
|
EndpointManager::_BindToEphemeral(TCPEndpoint* endpoint,
|
||||||
const sockaddr* address)
|
const sockaddr* address)
|
||||||
@@ -509,7 +509,7 @@ EndpointManager::Unbind(TCPEndpoint* endpoint)
|
|||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
MutexLocker _(fLock);
|
WriteLocker _(fLock);
|
||||||
|
|
||||||
if (!fEndpointHash.Remove(endpoint))
|
if (!fEndpointHash.Remove(endpoint))
|
||||||
panic("bound endpoint %p not in hash!", endpoint);
|
panic("bound endpoint %p not in hash!", endpoint);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
|
* Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <AddressUtilities.h>
|
#include <AddressUtilities.h>
|
||||||
|
|
||||||
#include <lock.h>
|
#include <lock.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
#include <util/DoublyLinkedList.h>
|
#include <util/DoublyLinkedList.h>
|
||||||
#include <util/MultiHashTable.h>
|
#include <util/MultiHashTable.h>
|
||||||
#include <util/OpenHashTable.h>
|
#include <util/OpenHashTable.h>
|
||||||
@@ -98,15 +99,15 @@ private:
|
|||||||
const sockaddr* peer);
|
const sockaddr* peer);
|
||||||
status_t _Bind(TCPEndpoint* endpoint,
|
status_t _Bind(TCPEndpoint* endpoint,
|
||||||
const sockaddr* address);
|
const sockaddr* address);
|
||||||
status_t _BindToAddress(TCPEndpoint* endpoint,
|
status_t _BindToAddress(WriteLocker& locker,
|
||||||
const sockaddr* address);
|
TCPEndpoint* endpoint, const sockaddr* address);
|
||||||
status_t _BindToEphemeral(TCPEndpoint* endpoint,
|
status_t _BindToEphemeral(TCPEndpoint* endpoint,
|
||||||
const sockaddr* address);
|
const sockaddr* address);
|
||||||
|
|
||||||
typedef OpenHashTable<ConnectionHashDefinition> ConnectionTable;
|
typedef OpenHashTable<ConnectionHashDefinition> ConnectionTable;
|
||||||
typedef MultiHashTable<EndpointHashDefinition> EndpointTable;
|
typedef MultiHashTable<EndpointHashDefinition> EndpointTable;
|
||||||
|
|
||||||
mutex fLock;
|
rw_lock fLock;
|
||||||
net_domain* fDomain;
|
net_domain* fDomain;
|
||||||
ConnectionTable fConnectionHash;
|
ConnectionTable fConnectionHash;
|
||||||
EndpointTable fEndpointHash;
|
EndpointTable fEndpointHash;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
|
* Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@@ -50,30 +50,34 @@ net_socket_module_info *gSocketModule;
|
|||||||
net_stack_module_info *gStackModule;
|
net_stack_module_info *gStackModule;
|
||||||
|
|
||||||
|
|
||||||
// TODO we need to think of a better way to do this. It would be
|
static EndpointManager* sEndpointManagers[AF_MAX];
|
||||||
// nice if we registered a per EndpointManager receiving
|
static rw_lock sEndpointManagersLock;
|
||||||
// protocol cookie, so we don't have to go through the list
|
|
||||||
// for each segment.
|
|
||||||
typedef DoublyLinkedList<EndpointManager> EndpointManagerList;
|
|
||||||
static mutex sEndpointManagersLock;
|
|
||||||
static EndpointManagerList sEndpointManagers;
|
|
||||||
|
|
||||||
|
|
||||||
// The TCP header length is at most 64 bytes.
|
// The TCP header length is at most 64 bytes.
|
||||||
static const int kMaxOptionSize = 64 - sizeof(tcp_header);
|
static const int kMaxOptionSize = 64 - sizeof(tcp_header);
|
||||||
|
|
||||||
|
|
||||||
static EndpointManager *
|
/*! Returns an endpoint manager for the specified domain, if any.
|
||||||
endpoint_manager_for(net_domain *domain)
|
You need to hold the sEndpointManagersLock when calling this function.
|
||||||
|
*/
|
||||||
|
static inline EndpointManager*
|
||||||
|
endpoint_manager_for_locked(int family)
|
||||||
{
|
{
|
||||||
EndpointManagerList::Iterator iterator = sEndpointManagers.GetIterator();
|
if (family >= AF_MAX || family < 0)
|
||||||
while (iterator.HasNext()) {
|
return NULL;
|
||||||
EndpointManager *endpointManager = iterator.Next();
|
|
||||||
if (endpointManager->Domain() == domain)
|
return sEndpointManagers[family];
|
||||||
return endpointManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
|
||||||
|
/*! Returns an endpoint manager for the specified domain, if any */
|
||||||
|
static inline EndpointManager*
|
||||||
|
endpoint_manager_for(net_domain* domain)
|
||||||
|
{
|
||||||
|
ReadLocker _(sEndpointManagersLock);
|
||||||
|
|
||||||
|
return endpoint_manager_for_locked(domain->family);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -251,10 +255,11 @@ dump_tcp_header(tcp_header &header)
|
|||||||
static int
|
static int
|
||||||
dump_endpoints(int argc, char** argv)
|
dump_endpoints(int argc, char** argv)
|
||||||
{
|
{
|
||||||
EndpointManagerList::Iterator iterator = sEndpointManagers.GetIterator();
|
for (int i = 0; i < AF_MAX; i++) {
|
||||||
|
EndpointManager* manager = sEndpointManagers[i];
|
||||||
while (iterator.HasNext())
|
if (manager != NULL)
|
||||||
iterator.Next()->Dump();
|
manager->Dump();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -278,13 +283,26 @@ dump_endpoint(int argc, char** argv)
|
|||||||
// #pragma mark - internal API
|
// #pragma mark - internal API
|
||||||
|
|
||||||
|
|
||||||
|
/*! Creates a new endpoint manager for the specified domain, or returns
|
||||||
|
an existing one for this domain.
|
||||||
|
*/
|
||||||
EndpointManager*
|
EndpointManager*
|
||||||
get_endpoint_manager(net_domain* domain)
|
get_endpoint_manager(net_domain* domain)
|
||||||
{
|
{
|
||||||
|
// See if there is one already
|
||||||
EndpointManager* endpointManager = endpoint_manager_for(domain);
|
EndpointManager* endpointManager = endpoint_manager_for(domain);
|
||||||
if (endpointManager)
|
if (endpointManager != NULL)
|
||||||
return endpointManager;
|
return endpointManager;
|
||||||
|
|
||||||
|
WriteLocker _(sEndpointManagersLock);
|
||||||
|
|
||||||
|
endpointManager = endpoint_manager_for_locked(domain->family);
|
||||||
|
if (endpointManager != NULL)
|
||||||
|
return endpointManager;
|
||||||
|
|
||||||
|
// There is no endpoint manager for this domain yet, so we need
|
||||||
|
// to create one.
|
||||||
|
|
||||||
endpointManager = new(std::nothrow) EndpointManager(domain);
|
endpointManager = new(std::nothrow) EndpointManager(domain);
|
||||||
if (endpointManager == NULL)
|
if (endpointManager == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -294,7 +312,7 @@ get_endpoint_manager(net_domain* domain)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sEndpointManagers.Add(endpointManager);
|
sEndpointManagers[domain->family] = endpointManager;
|
||||||
return endpointManager;
|
return endpointManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,9 +320,9 @@ get_endpoint_manager(net_domain* domain)
|
|||||||
void
|
void
|
||||||
put_endpoint_manager(EndpointManager* endpointManager)
|
put_endpoint_manager(EndpointManager* endpointManager)
|
||||||
{
|
{
|
||||||
// TODO: when the connection and endpoint count reach zero
|
// TODO: we may want to use reference counting instead of only discarding
|
||||||
// we should remove the endpoint manager from the endpoints
|
// them on unload. But since there is likely only IPv4/v6 there is not much
|
||||||
// list and delete it.
|
// point to it.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -386,7 +404,7 @@ add_tcp_header(net_address_module_info* addressModule,
|
|||||||
optionsLength);
|
optionsLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE(("add_tcp_header(): buffer %p, flags 0x%x, seq %lu, ack %lu, up %lu, "
|
TRACE(("add_tcp_header(): buffer %p, flags 0x%x, seq %lu, ack %lu, up %u, "
|
||||||
"win %u\n", buffer, segment.flags, segment.sequence,
|
"win %u\n", buffer, segment.flags, segment.sequence,
|
||||||
segment.acknowledge, segment.urgent_offset, segment.advertised_window));
|
segment.acknowledge, segment.urgent_offset, segment.advertised_window));
|
||||||
|
|
||||||
@@ -680,11 +698,11 @@ tcp_receive_data(net_buffer* buffer)
|
|||||||
bufferHeader.Remove(headerLength);
|
bufferHeader.Remove(headerLength);
|
||||||
// we no longer need to keep the header around
|
// we no longer need to keep the header around
|
||||||
|
|
||||||
MutexLocker _(sEndpointManagersLock);
|
|
||||||
|
|
||||||
EndpointManager* endpointManager = endpoint_manager_for(domain);
|
EndpointManager* endpointManager = endpoint_manager_for(domain);
|
||||||
if (endpointManager == NULL)
|
if (endpointManager == NULL) {
|
||||||
|
TRACE((" No endpoint manager!\n"));
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
int32 segmentAction = DROP;
|
int32 segmentAction = DROP;
|
||||||
|
|
||||||
@@ -695,11 +713,11 @@ tcp_receive_data(net_buffer* buffer)
|
|||||||
else if ((segment.flags & TCP_FLAG_RESET) == 0)
|
else if ((segment.flags & TCP_FLAG_RESET) == 0)
|
||||||
segmentAction = DROP | RESET;
|
segmentAction = DROP | RESET;
|
||||||
|
|
||||||
if (segmentAction & RESET) {
|
if ((segmentAction & RESET) != 0) {
|
||||||
// send reset
|
// send reset
|
||||||
endpointManager->ReplyWithReset(segment, buffer);
|
endpointManager->ReplyWithReset(segment, buffer);
|
||||||
}
|
}
|
||||||
if (segmentAction & DROP)
|
if ((segmentAction & DROP) != 0)
|
||||||
gBufferModule->free(buffer);
|
gBufferModule->free(buffer);
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -727,7 +745,7 @@ tcp_error_reply(net_protocol* protocol, net_buffer* causedError, uint32 code,
|
|||||||
static status_t
|
static status_t
|
||||||
tcp_init()
|
tcp_init()
|
||||||
{
|
{
|
||||||
mutex_init(&sEndpointManagersLock, "endpoint managers lock");
|
rw_lock_init(&sEndpointManagersLock, "endpoint managers");
|
||||||
|
|
||||||
status_t status = gStackModule->register_domain_protocols(AF_INET,
|
status_t status = gStackModule->register_domain_protocols(AF_INET,
|
||||||
SOCK_STREAM, 0,
|
SOCK_STREAM, 0,
|
||||||
@@ -764,7 +782,13 @@ tcp_uninit()
|
|||||||
{
|
{
|
||||||
remove_debugger_command("tcp_endpoint", dump_endpoint);
|
remove_debugger_command("tcp_endpoint", dump_endpoint);
|
||||||
remove_debugger_command("tcp_endpoints", dump_endpoints);
|
remove_debugger_command("tcp_endpoints", dump_endpoints);
|
||||||
mutex_destroy(&sEndpointManagersLock);
|
|
||||||
|
rw_lock_destroy(&sEndpointManagersLock);
|
||||||
|
|
||||||
|
for (int i = 0; i < AF_MAX; i++) {
|
||||||
|
delete sEndpointManagers[i];
|
||||||
|
}
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user