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:
parent
f7d2a39699
commit
9cbe6ce200
400
src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp
Normal file
400
src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp
Normal 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;
|
||||
}
|
||||
|
53
src/add-ons/kernel/network/protocols/tcp/EndpointManager.h
Normal file
53
src/add-ons/kernel/network/protocols/tcp/EndpointManager.h
Normal 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
|
@ -16,6 +16,7 @@ KernelAddon tcp :
|
||||
tcp.cpp
|
||||
TCPConnection.cpp
|
||||
BufferQueue.cpp
|
||||
EndpointManager.cpp
|
||||
;
|
||||
|
||||
# Installation
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user