made TCP handle multiple domains.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20718 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Hugo Santos
2007-04-16 00:27:39 +00:00
parent d5b5a2c2f8
commit 03d7f17e07
6 changed files with 218 additions and 150 deletions
@@ -26,6 +26,7 @@
struct connection_key { struct connection_key {
net_address_module_info *address_module;
const sockaddr *local; const sockaddr *local;
const sockaddr *peer; const sockaddr *peer;
}; };
@@ -42,7 +43,8 @@ static const uint16 kLastReservedPort = 1023;
static const uint16 kFirstEphemeralPort = 40000; static const uint16 kFirstEphemeralPort = 40000;
EndpointManager::EndpointManager() EndpointManager::EndpointManager(net_domain *domain)
: fDomain(domain)
{ {
fConnectionHash = hash_init(kConnectionHashBuckets, fConnectionHash = hash_init(kConnectionHashBuckets,
offsetof(TCPEndpoint, fConnectionHashNext), offsetof(TCPEndpoint, fConnectionHashNext),
@@ -89,6 +91,7 @@ TCPEndpoint *
EndpointManager::_LookupConnection(sockaddr *local, sockaddr *peer) EndpointManager::_LookupConnection(sockaddr *local, sockaddr *peer)
{ {
connection_key key; connection_key key;
key.address_module = AddressModule();
key.local = local; key.local = local;
key.peer = peer; key.peer = peer;
@@ -109,9 +112,6 @@ EndpointManager::_DumpConnections()
{ {
RecursiveLocker lock(&fLock); RecursiveLocker lock(&fLock);
if (gDomain == NULL)
return;
struct hash_iterator iterator; struct hash_iterator iterator;
hash_open(fConnectionHash, &iterator); hash_open(fConnectionHash, &iterator);
@@ -120,8 +120,8 @@ EndpointManager::_DumpConnections()
TCPEndpoint *endpoint; TCPEndpoint *endpoint;
while ((endpoint = (TCPEndpoint *)hash_next(fConnectionHash, &iterator)) != NULL) { while ((endpoint = (TCPEndpoint *)hash_next(fConnectionHash, &iterator)) != NULL) {
TRACE((" TCPEndpoint %p: local %s, peer %s\n", endpoint, TRACE((" TCPEndpoint %p: local %s, peer %s\n", endpoint,
AddressString(gDomain, (sockaddr *)&endpoint->socket->address, true).Data(), AddressString(Domain(), (sockaddr *)&endpoint->socket->address, true).Data(),
AddressString(gDomain, (sockaddr *)&endpoint->socket->peer, true).Data())); AddressString(Domain(), (sockaddr *)&endpoint->socket->peer, true).Data()));
} }
hash_close(fConnectionHash, &iterator, false); hash_close(fConnectionHash, &iterator, false);
@@ -138,13 +138,14 @@ EndpointManager::SetConnection(TCPEndpoint *endpoint,
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
if (gAddressModule->is_empty_address(local, false)) { if (AddressModule()->is_empty_address(local, false)) {
gAddressModule->set_to(&localBuffer, interfaceLocal); AddressModule()->set_to(&localBuffer, interfaceLocal);
gAddressModule->set_port(&localBuffer, gAddressModule->get_port(local)); AddressModule()->set_port(&localBuffer, AddressModule()->get_port(local));
local = &localBuffer; local = &localBuffer;
} }
connection_key key; connection_key key;
key.address_module = AddressModule();
key.local = local; key.local = local;
key.peer = peer; key.peer = peer;
@@ -153,8 +154,8 @@ EndpointManager::SetConnection(TCPEndpoint *endpoint,
_RemoveConnection(endpoint); _RemoveConnection(endpoint);
gAddressModule->set_to((sockaddr *)&endpoint->socket->address, local); AddressModule()->set_to((sockaddr *)&endpoint->socket->address, local);
gAddressModule->set_to((sockaddr *)&endpoint->socket->peer, peer); AddressModule()->set_to((sockaddr *)&endpoint->socket->peer, peer);
return hash_insert(fConnectionHash, endpoint); return hash_insert(fConnectionHash, endpoint);
} }
@@ -172,7 +173,7 @@ EndpointManager::FindConnection(sockaddr *local, sockaddr *peer)
// no explicit endpoint exists, check for wildcard endpoints // no explicit endpoint exists, check for wildcard endpoints
sockaddr wildcard; sockaddr wildcard;
gAddressModule->set_to_empty_address(&wildcard); AddressModule()->set_to_empty_address(&wildcard);
endpoint = _LookupConnection(local, &wildcard); endpoint = _LookupConnection(local, &wildcard);
if (endpoint != NULL) { if (endpoint != NULL) {
@@ -181,8 +182,8 @@ EndpointManager::FindConnection(sockaddr *local, sockaddr *peer)
} }
sockaddr localWildcard; sockaddr localWildcard;
gAddressModule->set_to_empty_address(&localWildcard); AddressModule()->set_to_empty_address(&localWildcard);
gAddressModule->set_port(&localWildcard, gAddressModule->get_port(local)); AddressModule()->set_port(&localWildcard, AddressModule()->get_port(local));
endpoint = _LookupConnection(&localWildcard, &wildcard); endpoint = _LookupConnection(&localWildcard, &wildcard);
if (endpoint != NULL) { if (endpoint != NULL) {
@@ -217,12 +218,12 @@ EndpointManager::Bind(TCPEndpoint *endpoint)
sockaddr *address = (sockaddr *)&endpoint->socket->address; sockaddr *address = (sockaddr *)&endpoint->socket->address;
TRACE(("EndpointManager::Bind(%p, %s)\n", endpoint, TRACE(("EndpointManager::Bind(%p, %s)\n", endpoint,
AddressString(gDomain, address, true).Data())); AddressString(Domain(), address, true).Data()));
if (gAddressModule->is_empty_address(address, true)) if (AddressModule()->is_empty_address(address, true))
return B_BAD_VALUE; return B_BAD_VALUE;
uint16 port = gAddressModule->get_port(address); uint16 port = AddressModule()->get_port(address);
// TODO: check the root group instead? // TODO: check the root group instead?
if (ntohs(port) <= kLastReservedPort && geteuid() != 0) if (ntohs(port) <= kLastReservedPort && geteuid() != 0)
@@ -245,7 +246,7 @@ EndpointManager::Bind(TCPEndpoint *endpoint)
TCPEndpoint *last = first; TCPEndpoint *last = first;
while (true) { while (true) {
// check if this endpoint binds to a wildcard address // check if this endpoint binds to a wildcard address
if (gAddressModule->is_empty_address((sockaddr *)&last->socket->address, false)) { if (AddressModule()->is_empty_address((sockaddr *)&last->socket->address, false)) {
// you cannot specialize a wildcard endpoint - you have to open the // you cannot specialize a wildcard endpoint - you have to open the
// wildcard endpoint last // wildcard endpoint last
return B_PERMISSION_DENIED; return B_PERMISSION_DENIED;
@@ -293,9 +294,9 @@ EndpointManager::BindToEphemeral(TCPEndpoint *endpoint)
TCPEndpoint *other = _LookupEndpoint(port); TCPEndpoint *other = _LookupEndpoint(port);
if (other == NULL) { if (other == NULL) {
// found a port // found a port
gAddressModule->set_port((sockaddr *)&endpoint->socket->address, port); AddressModule()->set_port((sockaddr *)&endpoint->socket->address, port);
TRACE((" EndpointManager::BindToEphemeral(%p) -> %s\n", endpoint, TRACE((" EndpointManager::BindToEphemeral(%p) -> %s\n", endpoint,
AddressString(gDomain, (sockaddr *)&endpoint->socket->address, true).Data())); AddressString(Domain(), (sockaddr *)&endpoint->socket->address, true).Data()));
endpoint->fEndpointNextWithSamePort = NULL; endpoint->fEndpointNextWithSamePort = NULL;
hash_insert(fEndpointHash, endpoint); hash_insert(fEndpointHash, endpoint);
hash_insert(fConnectionHash, endpoint); hash_insert(fConnectionHash, endpoint);
@@ -320,7 +321,7 @@ EndpointManager::Unbind(TCPEndpoint *endpoint)
RecursiveLocker locker(&fLock); RecursiveLocker locker(&fLock);
if (!endpoint->fSpawned) { if (!endpoint->fSpawned) {
TCPEndpoint *other = _LookupEndpoint(gAddressModule->get_port( TCPEndpoint *other = _LookupEndpoint(AddressModule()->get_port(
(sockaddr *)&endpoint->socket->address)); (sockaddr *)&endpoint->socket->address));
if (other != endpoint) { if (other != endpoint) {
// remove endpoint from the list of endpoints with the same port // remove endpoint from the list of endpoints with the same port
@@ -351,6 +352,45 @@ EndpointManager::Unbind(TCPEndpoint *endpoint)
} }
status_t
EndpointManager::ReplyWithReset(tcp_segment_header &segment,
net_buffer *buffer)
{
TRACE(("TCP: Sending RST...\n"));
net_buffer *reply = gBufferModule->create(512);
if (reply == NULL)
return B_NO_MEMORY;
AddressModule()->set_to((sockaddr *)&reply->source,
(sockaddr *)&buffer->destination);
AddressModule()->set_to((sockaddr *)&reply->destination,
(sockaddr *)&buffer->source);
tcp_segment_header outSegment;
outSegment.flags = TCP_FLAG_RESET;
outSegment.sequence = 0;
outSegment.acknowledge = 0;
outSegment.advertised_window = 0;
outSegment.urgent_offset = 0;
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) == 0) {
outSegment.flags |= TCP_FLAG_ACKNOWLEDGE;
outSegment.acknowledge = segment.sequence + buffer->size;
} else
outSegment.sequence = segment.acknowledge;
status_t status = add_tcp_header(AddressModule(), outSegment, reply);
if (status == B_OK)
status = Domain()->module->send_data(NULL, reply);
if (status != B_OK)
gBufferModule->free(reply);
return status;
}
// #pragma mark - hash functions // #pragma mark - hash functions
@@ -360,9 +400,9 @@ EndpointManager::_ConnectionCompare(void *_endpoint, const void *_key)
const connection_key *key = (connection_key *)_key; const connection_key *key = (connection_key *)_key;
TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint; TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint;
if (gAddressModule->equal_addresses_and_ports(key->local, if (key->address_module->equal_addresses_and_ports(key->local,
(sockaddr *)&endpoint->socket->address) (sockaddr *)&endpoint->socket->address)
&& gAddressModule->equal_addresses_and_ports(key->peer, && key->address_module->equal_addresses_and_ports(key->peer,
(sockaddr *)&endpoint->socket->peer)) (sockaddr *)&endpoint->socket->peer))
return 0; return 0;
@@ -373,20 +413,23 @@ EndpointManager::_ConnectionCompare(void *_endpoint, const void *_key)
/*static*/ uint32 /*static*/ uint32
EndpointManager::_ConnectionHash(void *_endpoint, const void *_key, uint32 range) EndpointManager::_ConnectionHash(void *_endpoint, const void *_key, uint32 range)
{ {
net_address_module_info *address_module;
const sockaddr *local; const sockaddr *local;
const sockaddr *peer; const sockaddr *peer;
if (_endpoint != NULL) { if (_endpoint != NULL) {
TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint; TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint;
address_module = endpoint->AddressModule();
local = (sockaddr *)&endpoint->socket->address; local = (sockaddr *)&endpoint->socket->address;
peer = (sockaddr *)&endpoint->socket->peer; peer = (sockaddr *)&endpoint->socket->peer;
} else { } else {
const connection_key *key = (connection_key *)_key; const connection_key *key = (connection_key *)_key;
address_module = key->address_module;
local = key->local; local = key->local;
peer = key->peer; peer = key->peer;
} }
return gAddressModule->hash_address_pair(local, peer) % range; return address_module->hash_address_pair(local, peer) % range;
} }
@@ -396,8 +439,8 @@ EndpointManager::_EndpointCompare(void *_endpoint, const void *_key)
const endpoint_key *key = (endpoint_key *)_key; const endpoint_key *key = (endpoint_key *)_key;
TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint; TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint;
return gAddressModule->get_port((sockaddr *)&endpoint->socket->address) return endpoint->AddressModule()->get_port(
== key->port ? 0 : 1; (sockaddr *)&endpoint->socket->address) == key->port ? 0 : 1;
} }
@@ -406,7 +449,8 @@ EndpointManager::_EndpointHash(void *_endpoint, const void *_key, uint32 range)
{ {
if (_endpoint != NULL) { if (_endpoint != NULL) {
TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint; TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint;
return gAddressModule->get_port((sockaddr *)&endpoint->socket->address) % range; return endpoint->AddressModule()->get_port(
(sockaddr *)&endpoint->socket->address) % range;
} }
const endpoint_key *key = (endpoint_key *)_key; const endpoint_key *key = (endpoint_key *)_key;
@@ -8,18 +8,24 @@
#ifndef ENDPOINT_MANAGER_H #ifndef ENDPOINT_MANAGER_H
#define ENDPOINT_MANAGER_H #define ENDPOINT_MANAGER_H
#include "tcp.h"
#include <net_datalink.h>
#include <lock.h> #include <lock.h>
#include <util/DoublyLinkedList.h>
#include <util/khash.h> #include <util/khash.h>
#include <sys/socket.h> #include <sys/socket.h>
struct net_address_module_info;
struct net_domain;
class TCPEndpoint; class TCPEndpoint;
class EndpointManager { class EndpointManager : public DoublyLinkedListLinkImpl<EndpointManager> {
public: public:
EndpointManager(); EndpointManager(net_domain *domain);
~EndpointManager(); ~EndpointManager();
status_t InitCheck() const; status_t InitCheck() const;
@@ -34,6 +40,13 @@ class EndpointManager {
status_t BindToEphemeral(TCPEndpoint *endpoint); status_t BindToEphemeral(TCPEndpoint *endpoint);
status_t Unbind(TCPEndpoint *endpoint); status_t Unbind(TCPEndpoint *endpoint);
status_t ReplyWithReset(tcp_segment_header &segment,
net_buffer *buffer);
net_domain *Domain() const { return fDomain; }
net_address_module_info *AddressModule() const
{ return Domain()->address_module; }
private: private:
TCPEndpoint *_LookupConnection(sockaddr *local, sockaddr *peer); TCPEndpoint *_LookupConnection(sockaddr *local, sockaddr *peer);
status_t _RemoveConnection(TCPEndpoint *endpoint); status_t _RemoveConnection(TCPEndpoint *endpoint);
@@ -45,6 +58,8 @@ class EndpointManager {
static int _EndpointCompare(void *_endpoint, const void *_key); static int _EndpointCompare(void *_endpoint, const void *_key);
static uint32 _EndpointHash(void *_endpoint, const void *_key, uint32 range); static uint32 _EndpointHash(void *_endpoint, const void *_key, uint32 range);
net_domain *fDomain;
hash_table *fConnectionHash; hash_table *fConnectionHash;
hash_table *fEndpointHash; hash_table *fEndpointHash;
recursive_lock fLock; recursive_lock fLock;
@@ -158,6 +158,7 @@ WaitList::Signal()
TCPEndpoint::TCPEndpoint(net_socket *socket) TCPEndpoint::TCPEndpoint(net_socket *socket)
: :
fManager(NULL),
fReceiveList("tcp receive"), fReceiveList("tcp receive"),
fSendList("tcp send"), fSendList("tcp send"),
fOptions(0), fOptions(0),
@@ -205,7 +206,10 @@ TCPEndpoint::~TCPEndpoint()
gStackModule->cancel_timer(&fDelayedAcknowledgeTimer); gStackModule->cancel_timer(&fDelayedAcknowledgeTimer);
gStackModule->cancel_timer(&fTimeWaitTimer); gStackModule->cancel_timer(&fTimeWaitTimer);
gEndpointManager->Unbind(this); if (fManager) {
fManager->Unbind(this);
return_endpoint_manager(fManager);
}
recursive_lock_destroy(&fLock); recursive_lock_destroy(&fLock);
} }
@@ -234,7 +238,14 @@ status_t
TCPEndpoint::Open() TCPEndpoint::Open()
{ {
TRACE("Open()"); TRACE("Open()");
// nothing to do here...
if (Domain() == NULL || AddressModule() == NULL)
return EAFNOSUPPORT;
fManager = create_endpoint_manager(Domain());
if (fManager == NULL)
return EAFNOSUPPORT;
return B_OK; return B_OK;
} }
@@ -304,7 +315,7 @@ status_t
TCPEndpoint::Connect(const struct sockaddr *address) TCPEndpoint::Connect(const struct sockaddr *address)
{ {
TRACE("Connect() on address %s", TRACE("Connect() on address %s",
AddressString(gDomain, address, true).Data()); AddressString(Domain(), address, true).Data());
RecursiveLocker locker(&fLock); RecursiveLocker locker(&fLock);
@@ -319,14 +330,14 @@ TCPEndpoint::Connect(const struct sockaddr *address)
// get a net_route if there isn't one // get a net_route if there isn't one
// TODO: get a net_route_info instead! // TODO: get a net_route_info instead!
if (fRoute == NULL) { if (fRoute == NULL) {
fRoute = gDatalinkModule->get_route(gDomain, (sockaddr *)address); fRoute = gDatalinkModule->get_route(Domain(), (sockaddr *)address);
TRACE(" Connect(): Using Route %p", fRoute); TRACE(" Connect(): Using Route %p", fRoute);
if (fRoute == NULL) if (fRoute == NULL)
return ENETUNREACH; return ENETUNREACH;
} }
// make sure connection does not already exist // make sure connection does not already exist
status_t status = gEndpointManager->SetConnection(this, status_t status = fManager->SetConnection(this,
(sockaddr *)&socket->address, address, fRoute->interface->address); (sockaddr *)&socket->address, address, fRoute->interface->address);
if (status < B_OK) { if (status < B_OK) {
TRACE(" Connect(): could not add connection: %s!", strerror(status)); TRACE(" Connect(): could not add connection: %s!", strerror(status));
@@ -413,7 +424,7 @@ TCPEndpoint::Bind(sockaddr *address)
return B_BAD_VALUE; return B_BAD_VALUE;
TRACE("Bind() on address %s", TRACE("Bind() on address %s",
AddressString(gDomain, address, true).Data()); AddressString(Domain(), address, true).Data());
RecursiveLocker lock(fLock); RecursiveLocker lock(fLock);
@@ -425,13 +436,13 @@ TCPEndpoint::Bind(sockaddr *address)
if (status < B_OK) if (status < B_OK)
return status; return status;
if (gAddressModule->get_port(address) == 0) if (AddressModule()->get_port(address) == 0)
status = gEndpointManager->BindToEphemeral(this); status = fManager->BindToEphemeral(this);
else else
status = gEndpointManager->Bind(this); status = fManager->Bind(this);
TRACE(" Bind() bound to %s (status %i)", TRACE(" Bind() bound to %s (status %i)",
AddressString(gDomain, (sockaddr *)&socket->address, true).Data(), AddressString(Domain(), (sockaddr *)&socket->address, true).Data(),
(int)status); (int)status);
return status; return status;
@@ -444,7 +455,7 @@ TCPEndpoint::Unbind(struct sockaddr *address)
TRACE("Unbind()"); TRACE("Unbind()");
RecursiveLocker lock(fLock); RecursiveLocker lock(fLock);
return gEndpointManager->Unbind(this); return fManager->Unbind(this);
} }
@@ -646,7 +657,15 @@ TCPEndpoint::ReadAvailable()
bool bool
TCPEndpoint::IsBound() const TCPEndpoint::IsBound() const
{ {
return !gAddressModule->is_empty_address((sockaddr *)&socket->address, true); return !AddressModule()->is_empty_address((sockaddr *)&socket->address, true);
}
void
TCPEndpoint::DeleteSocket()
{
// the next call will delete `this'.
gSocketModule->delete_socket(socket);
} }
@@ -723,9 +742,9 @@ TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
if (gSocketModule->spawn_pending_socket(socket, &newSocket) < B_OK) if (gSocketModule->spawn_pending_socket(socket, &newSocket) < B_OK)
return DROP; return DROP;
gAddressModule->set_to((sockaddr *)&newSocket->address, AddressModule()->set_to((sockaddr *)&newSocket->address,
(sockaddr *)&buffer->destination); (sockaddr *)&buffer->destination);
gAddressModule->set_to((sockaddr *)&newSocket->peer, AddressModule()->set_to((sockaddr *)&newSocket->peer,
(sockaddr *)&buffer->source); (sockaddr *)&buffer->source);
TCPEndpoint *endpoint = (TCPEndpoint *)newSocket->first_protocol; TCPEndpoint *endpoint = (TCPEndpoint *)newSocket->first_protocol;
@@ -734,12 +753,12 @@ TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
// TODO: proper error handling! // TODO: proper error handling!
endpoint->fRoute = gDatalinkModule->get_route(gDomain, endpoint->fRoute = gDatalinkModule->get_route(Domain(),
(sockaddr *)&newSocket->peer); (sockaddr *)&newSocket->peer);
if (endpoint->fRoute == NULL) if (endpoint->fRoute == NULL)
return DROP; return DROP;
if (gEndpointManager->SetConnection(endpoint, (sockaddr *)&buffer->destination, if (fManager->SetConnection(endpoint, (sockaddr *)&buffer->destination,
(sockaddr *)&buffer->source, NULL) < B_OK) (sockaddr *)&buffer->source, NULL) < B_OK)
return DROP; return DROP;
@@ -1085,8 +1104,8 @@ TCPEndpoint::_SendQueued(bool force)
return status; return status;
} }
gAddressModule->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address); AddressModule()->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address);
gAddressModule->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer); AddressModule()->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer);
uint32 size = buffer->size; uint32 size = buffer->size;
if (length > 0 && fSendNext + segmentLength == fSendQueue.LastSequence()) { if (length > 0 && fSendNext + segmentLength == fSendQueue.LastSequence()) {
@@ -1108,10 +1127,10 @@ TCPEndpoint::_SendQueued(bool force)
TRACE("SendQueued() flags %x, buffer %p, size %lu, from address %s to %s", TRACE("SendQueued() flags %x, buffer %p, size %lu, from address %s to %s",
segment.flags, buffer, buffer->size, segment.flags, buffer, buffer->size,
AddressString(gDomain, (sockaddr *)&buffer->source, true).Data(), AddressString(Domain(), (sockaddr *)&buffer->source, true).Data(),
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data()); AddressString(Domain(), (sockaddr *)&buffer->destination, true).Data());
status = add_tcp_header(segment, buffer); status = add_tcp_header(AddressModule(), segment, buffer);
if (status != B_OK) { if (status != B_OK) {
gBufferModule->free(buffer); gBufferModule->free(buffer);
return status; return status;
@@ -1525,6 +1544,6 @@ TCPEndpoint::_TimeWaitTimer(struct net_timer *timer, void *data)
if (recursive_lock_lock(&endpoint->Lock()) < B_OK) if (recursive_lock_lock(&endpoint->Lock()) < B_OK)
return; return;
gSocketModule->delete_socket(endpoint->socket); endpoint->DeleteSocket();
} }
@@ -64,6 +64,8 @@ class TCPEndpoint : public net_protocol {
tcp_state State() const { return fState; } tcp_state State() const { return fState; }
bool IsBound() const; bool IsBound() const;
void DeleteSocket();
status_t DelayedAcknowledge(); status_t DelayedAcknowledge();
status_t SendAcknowledge(); status_t SendAcknowledge();
status_t UpdateTimeWait(); status_t UpdateTimeWait();
@@ -72,6 +74,12 @@ class TCPEndpoint : public net_protocol {
net_buffer *buffer); net_buffer *buffer);
int32 Receive(tcp_segment_header& segment, net_buffer *buffer); int32 Receive(tcp_segment_header& segment, net_buffer *buffer);
net_domain *Domain() const
{ return socket->first_protocol->module->get_domain(
socket->first_protocol); }
net_address_module_info *AddressModule() const
{ return Domain()->address_module; }
private: private:
friend class EndpointManager; friend class EndpointManager;
@@ -93,6 +101,8 @@ class TCPEndpoint : public net_protocol {
static void _PersistTimer(net_timer *timer, void *data); static void _PersistTimer(net_timer *timer, void *data);
static void _DelayedAcknowledgeTimer(net_timer *timer, void *data); static void _DelayedAcknowledgeTimer(net_timer *timer, void *data);
EndpointManager *fManager;
TCPEndpoint *fConnectionHashNext; TCPEndpoint *fConnectionHashNext;
TCPEndpoint *fEndpointHashNext; TCPEndpoint *fEndpointHashNext;
TCPEndpoint *fEndpointNextWithSamePort; TCPEndpoint *fEndpointNextWithSamePort;
@@ -42,34 +42,56 @@
typedef NetBufferField<uint16, offsetof(tcp_header, checksum)> TCPChecksumField; typedef NetBufferField<uint16, offsetof(tcp_header, checksum)> TCPChecksumField;
net_domain *gDomain;
net_address_module_info *gAddressModule;
net_buffer_module_info *gBufferModule; net_buffer_module_info *gBufferModule;
net_datalink_module_info *gDatalinkModule; net_datalink_module_info *gDatalinkModule;
net_socket_module_info *gSocketModule; net_socket_module_info *gSocketModule;
net_stack_module_info *gStackModule; net_stack_module_info *gStackModule;
EndpointManager *gEndpointManager;
status_t // TODO we need to think of a better way to do this. It would be
set_domain(net_interface *interface = NULL) // nice if we registered a per EndpointManager receiving
// protocol cookie, so we don't have to go through the list
// for each segment.
typedef DoublyLinkedList<EndpointManager> EndpointManagerList;
static benaphore sEndpointManagersLock;
static EndpointManagerList sEndpointManagers;
static EndpointManager *
endpoint_manager_for(net_domain *domain)
{ {
if (gDomain == NULL) { EndpointManagerList::Iterator iterator = sEndpointManagers.GetIterator();
// domain and address module are not known yet, we copy them from while (iterator.HasNext()) {
// the buffer's interface (if any): EndpointManager *endpointManager = iterator.Next();
if (interface == NULL || interface->domain == NULL) if (endpointManager->Domain() == domain)
gDomain = gStackModule->get_domain(AF_INET); return endpointManager;
else
gDomain = interface->domain;
if (gDomain == NULL) {
// this shouldn't occur, of course, but who knows...
return B_BAD_VALUE;
}
gAddressModule = gDomain->address_module;
} }
return B_OK; return NULL;
}
EndpointManager *
create_endpoint_manager(net_domain *domain)
{
EndpointManager *endpointManager = endpoint_manager_for(domain);
if (endpointManager)
return endpointManager;
endpointManager = new (std::nothrow) EndpointManager(domain);
if (endpointManager)
sEndpointManagers.Add(endpointManager);
return endpointManager;
}
void
return_endpoint_manager(EndpointManager *endpointManager)
{
// TODO when the connection and endpoint count reach zero
// we should remove the endpoint manager from the endpoints
// list and delete it.
} }
@@ -125,7 +147,8 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
for \a flags, \a seq \a ack and \a advertisedWindow. for \a flags, \a seq \a ack and \a advertisedWindow.
*/ */
status_t status_t
add_tcp_header(tcp_segment_header &segment, net_buffer *buffer) add_tcp_header(net_address_module_info *addressModule,
tcp_segment_header &segment, net_buffer *buffer)
{ {
buffer->protocol = IPPROTO_TCP; buffer->protocol = IPPROTO_TCP;
@@ -138,8 +161,8 @@ add_tcp_header(tcp_segment_header &segment, net_buffer *buffer)
tcp_header &header = bufferHeader.Data(); tcp_header &header = bufferHeader.Data();
header.source_port = gAddressModule->get_port((sockaddr *)&buffer->source); header.source_port = addressModule->get_port((sockaddr *)&buffer->source);
header.destination_port = gAddressModule->get_port((sockaddr *)&buffer->destination); header.destination_port = addressModule->get_port((sockaddr *)&buffer->destination);
header.sequence = htonl(segment.sequence); header.sequence = htonl(segment.sequence);
header.acknowledge = (segment.flags & TCP_FLAG_ACKNOWLEDGE) header.acknowledge = (segment.flags & TCP_FLAG_ACKNOWLEDGE)
? htonl(segment.acknowledge) : 0; ? htonl(segment.acknowledge) : 0;
@@ -161,7 +184,7 @@ add_tcp_header(tcp_segment_header &segment, net_buffer *buffer)
TRACE(("add_tcp_header(): buffer %p, flags 0x%x, seq %lu, ack %lu, win %u\n", buffer, TRACE(("add_tcp_header(): buffer %p, flags 0x%x, seq %lu, ack %lu, win %u\n", buffer,
segment.flags, segment.sequence, segment.acknowledge, segment.advertised_window)); segment.flags, segment.sequence, segment.acknowledge, segment.advertised_window));
*TCPChecksumField(buffer) = Checksum::PseudoHeader(gAddressModule, *TCPChecksumField(buffer) = Checksum::PseudoHeader(addressModule,
gBufferModule, buffer, IPPROTO_TCP); gBufferModule, buffer, IPPROTO_TCP);
return B_OK; return B_OK;
@@ -222,44 +245,6 @@ process_options(tcp_segment_header &segment, net_buffer *buffer, int32 size)
} }
status_t
reply_with_reset(tcp_segment_header &segment, net_buffer *buffer)
{
TRACE(("TCP: Sending RST...\n"));
net_buffer *reply = gBufferModule->create(512);
if (reply == NULL)
return B_NO_MEMORY;
gAddressModule->set_to((sockaddr *)&reply->source,
(sockaddr *)&buffer->destination);
gAddressModule->set_to((sockaddr *)&reply->destination,
(sockaddr *)&buffer->source);
tcp_segment_header outSegment;
outSegment.flags = TCP_FLAG_RESET;
outSegment.sequence = 0;
outSegment.acknowledge = 0;
outSegment.advertised_window = 0;
outSegment.urgent_offset = 0;
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) == 0) {
outSegment.flags |= TCP_FLAG_ACKNOWLEDGE;
outSegment.acknowledge = segment.sequence + buffer->size;
} else
outSegment.sequence = segment.acknowledge;
status_t status = add_tcp_header(outSegment, reply);
if (status == B_OK)
status = gDomain->module->send_data(NULL, reply);
if (status != B_OK)
gBufferModule->free(reply);
return status;
}
const char * const char *
name_for_state(tcp_state state) name_for_state(tcp_state state)
{ {
@@ -350,9 +335,6 @@ tcp_uninit_protocol(net_protocol *protocol)
status_t status_t
tcp_open(net_protocol *protocol) tcp_open(net_protocol *protocol)
{ {
if (gDomain == NULL && set_domain() != B_OK)
return B_ERROR;
return ((TCPEndpoint *)protocol)->Open(); return ((TCPEndpoint *)protocol)->Open();
} }
@@ -497,9 +479,12 @@ tcp_receive_data(net_buffer *buffer)
{ {
TRACE(("TCP: Received buffer %p\n", buffer)); TRACE(("TCP: Received buffer %p\n", buffer));
if (gDomain == NULL && set_domain(buffer->interface) != B_OK) if (buffer->interface == NULL || buffer->interface->domain == NULL)
return B_ERROR; return B_ERROR;
net_domain *domain = buffer->interface->domain;
net_address_module_info *addressModule = domain->address_module;
NetBufferHeaderReader<tcp_header> bufferHeader(buffer); NetBufferHeaderReader<tcp_header> bufferHeader(buffer);
if (bufferHeader.Status() < B_OK) if (bufferHeader.Status() < B_OK)
return bufferHeader.Status(); return bufferHeader.Status();
@@ -510,16 +495,17 @@ tcp_receive_data(net_buffer *buffer)
if (headerLength < sizeof(tcp_header)) if (headerLength < sizeof(tcp_header))
return B_BAD_DATA; return B_BAD_DATA;
if (Checksum::PseudoHeader(gAddressModule, gBufferModule, buffer, if (Checksum::PseudoHeader(addressModule, gBufferModule, buffer,
IPPROTO_TCP) != 0) IPPROTO_TCP) != 0)
return B_BAD_DATA; return B_BAD_DATA;
gAddressModule->set_port((struct sockaddr *)&buffer->source, header.source_port); addressModule->set_port((sockaddr *)&buffer->source, header.source_port);
gAddressModule->set_port((struct sockaddr *)&buffer->destination, header.destination_port); addressModule->set_port((sockaddr *)&buffer->destination,
header.destination_port);
TRACE((" Looking for: peer %s, local %s\n", TRACE((" Looking for: peer %s, local %s\n",
AddressString(gDomain, (sockaddr *)&buffer->source, true).Data(), AddressString(domain, (sockaddr *)&buffer->source, true).Data(),
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data())); AddressString(domain, (sockaddr *)&buffer->destination, true).Data()));
//dump_tcp_header(header); //dump_tcp_header(header);
//gBufferModule->dump(buffer); //gBufferModule->dump(buffer);
@@ -538,11 +524,17 @@ 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
RecursiveLocker locker(gEndpointManager->Locker()); BenaphoreLocker _(sEndpointManagersLock);
EndpointManager *endpointManager = endpoint_manager_for(domain);
if (endpointManager == NULL)
return B_ERROR;
RecursiveLocker locker(endpointManager->Locker());
int32 segmentAction = DROP; int32 segmentAction = DROP;
TCPEndpoint *endpoint = gEndpointManager->FindConnection( TCPEndpoint *endpoint = endpointManager->FindConnection(
(struct sockaddr *)&buffer->destination, (struct sockaddr *)&buffer->source); (sockaddr *)&buffer->destination, (sockaddr *)&buffer->source);
if (endpoint != NULL) { if (endpoint != NULL) {
RecursiveLocker locker(endpoint->Lock()); RecursiveLocker locker(endpoint->Lock());
TRACE(("Endpoint %p in state %s\n", endpoint, name_for_state(endpoint->State()))); TRACE(("Endpoint %p in state %s\n", endpoint, name_for_state(endpoint->State())));
@@ -575,13 +567,13 @@ tcp_receive_data(net_buffer *buffer)
else if (segmentAction & ACKNOWLEDGE) else if (segmentAction & ACKNOWLEDGE)
endpoint->DelayedAcknowledge(); endpoint->DelayedAcknowledge();
else if (segmentAction & DELETE) else if (segmentAction & DELETE)
gSocketModule->delete_socket(endpoint->socket); endpoint->DeleteSocket();
} 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) {
// send reset // send reset
reply_with_reset(segment, buffer); endpointManager->ReplyWithReset(segment, buffer);
} }
if (segmentAction & DROP) if (segmentAction & DROP)
gBufferModule->free(buffer); gBufferModule->free(buffer);
@@ -611,52 +603,39 @@ tcp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
static status_t static status_t
tcp_init() tcp_init()
{ {
status_t status; status_t status = benaphore_init(&sEndpointManagersLock,
"endpoint managers lock");
gDomain = NULL;
gAddressModule = NULL;
gEndpointManager = new (std::nothrow) EndpointManager();
if (gEndpointManager == NULL)
return B_NO_MEMORY;
status = gEndpointManager->InitCheck();
if (status < B_OK) if (status < B_OK)
goto err1; return status;
status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, 0, status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, 0,
"network/protocols/tcp/v1", "network/protocols/tcp/v1",
"network/protocols/ipv4/v1", "network/protocols/ipv4/v1",
NULL); NULL);
if (status < B_OK) if (status < B_OK)
goto err1; return status;
status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_TCP, status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_TCP,
"network/protocols/tcp/v1", "network/protocols/tcp/v1",
"network/protocols/ipv4/v1", "network/protocols/ipv4/v1",
NULL); NULL);
if (status < B_OK) if (status < B_OK)
goto err1; return status;
status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP, status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP,
"network/protocols/tcp/v1"); "network/protocols/tcp/v1");
if (status < B_OK) if (status < B_OK)
goto err1; return status;
return B_OK; return B_OK;
err1:
delete gEndpointManager;
TRACE(("init_tcp() fails with %lx (%s)\n", status, strerror(status)));
return status;
} }
static status_t static status_t
tcp_uninit() tcp_uninit()
{ {
delete gEndpointManager; benaphore_destroy(&sEndpointManagersLock);
return B_OK; return B_OK;
} }
@@ -157,17 +157,18 @@ enum tcp_segment_action {
}; };
extern net_domain *gDomain;
extern net_address_module_info *gAddressModule;
extern net_buffer_module_info *gBufferModule; extern net_buffer_module_info *gBufferModule;
extern net_datalink_module_info *gDatalinkModule; extern net_datalink_module_info *gDatalinkModule;
extern net_socket_module_info *gSocketModule; extern net_socket_module_info *gSocketModule;
extern net_stack_module_info *gStackModule; extern net_stack_module_info *gStackModule;
extern EndpointManager *gEndpointManager;
status_t add_tcp_header(tcp_segment_header &segment, net_buffer *buffer); status_t add_tcp_header(net_address_module_info *addressModule,
tcp_segment_header &segment, net_buffer *buffer);
const char *name_for_state(tcp_state state); const char *name_for_state(tcp_state state);
EndpointManager *create_endpoint_manager(net_domain *domain);
void return_endpoint_manager(EndpointManager *);
#endif // TCP_H #endif // TCP_H