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:
@@ -26,6 +26,7 @@
|
||||
|
||||
|
||||
struct connection_key {
|
||||
net_address_module_info *address_module;
|
||||
const sockaddr *local;
|
||||
const sockaddr *peer;
|
||||
};
|
||||
@@ -42,7 +43,8 @@ static const uint16 kLastReservedPort = 1023;
|
||||
static const uint16 kFirstEphemeralPort = 40000;
|
||||
|
||||
|
||||
EndpointManager::EndpointManager()
|
||||
EndpointManager::EndpointManager(net_domain *domain)
|
||||
: fDomain(domain)
|
||||
{
|
||||
fConnectionHash = hash_init(kConnectionHashBuckets,
|
||||
offsetof(TCPEndpoint, fConnectionHashNext),
|
||||
@@ -89,6 +91,7 @@ TCPEndpoint *
|
||||
EndpointManager::_LookupConnection(sockaddr *local, sockaddr *peer)
|
||||
{
|
||||
connection_key key;
|
||||
key.address_module = AddressModule();
|
||||
key.local = local;
|
||||
key.peer = peer;
|
||||
|
||||
@@ -109,9 +112,6 @@ EndpointManager::_DumpConnections()
|
||||
{
|
||||
RecursiveLocker lock(&fLock);
|
||||
|
||||
if (gDomain == NULL)
|
||||
return;
|
||||
|
||||
struct hash_iterator iterator;
|
||||
hash_open(fConnectionHash, &iterator);
|
||||
|
||||
@@ -120,8 +120,8 @@ EndpointManager::_DumpConnections()
|
||||
TCPEndpoint *endpoint;
|
||||
while ((endpoint = (TCPEndpoint *)hash_next(fConnectionHash, &iterator)) != NULL) {
|
||||
TRACE((" TCPEndpoint %p: local %s, peer %s\n", endpoint,
|
||||
AddressString(gDomain, (sockaddr *)&endpoint->socket->address, true).Data(),
|
||||
AddressString(gDomain, (sockaddr *)&endpoint->socket->peer, true).Data()));
|
||||
AddressString(Domain(), (sockaddr *)&endpoint->socket->address, true).Data(),
|
||||
AddressString(Domain(), (sockaddr *)&endpoint->socket->peer, true).Data()));
|
||||
}
|
||||
|
||||
hash_close(fConnectionHash, &iterator, false);
|
||||
@@ -138,13 +138,14 @@ EndpointManager::SetConnection(TCPEndpoint *endpoint,
|
||||
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));
|
||||
if (AddressModule()->is_empty_address(local, false)) {
|
||||
AddressModule()->set_to(&localBuffer, interfaceLocal);
|
||||
AddressModule()->set_port(&localBuffer, AddressModule()->get_port(local));
|
||||
local = &localBuffer;
|
||||
}
|
||||
|
||||
connection_key key;
|
||||
key.address_module = AddressModule();
|
||||
key.local = local;
|
||||
key.peer = peer;
|
||||
|
||||
@@ -153,8 +154,8 @@ EndpointManager::SetConnection(TCPEndpoint *endpoint,
|
||||
|
||||
_RemoveConnection(endpoint);
|
||||
|
||||
gAddressModule->set_to((sockaddr *)&endpoint->socket->address, local);
|
||||
gAddressModule->set_to((sockaddr *)&endpoint->socket->peer, peer);
|
||||
AddressModule()->set_to((sockaddr *)&endpoint->socket->address, local);
|
||||
AddressModule()->set_to((sockaddr *)&endpoint->socket->peer, peer);
|
||||
|
||||
return hash_insert(fConnectionHash, endpoint);
|
||||
}
|
||||
@@ -172,7 +173,7 @@ EndpointManager::FindConnection(sockaddr *local, sockaddr *peer)
|
||||
// no explicit endpoint exists, check for wildcard endpoints
|
||||
|
||||
sockaddr wildcard;
|
||||
gAddressModule->set_to_empty_address(&wildcard);
|
||||
AddressModule()->set_to_empty_address(&wildcard);
|
||||
|
||||
endpoint = _LookupConnection(local, &wildcard);
|
||||
if (endpoint != NULL) {
|
||||
@@ -181,8 +182,8 @@ EndpointManager::FindConnection(sockaddr *local, sockaddr *peer)
|
||||
}
|
||||
|
||||
sockaddr localWildcard;
|
||||
gAddressModule->set_to_empty_address(&localWildcard);
|
||||
gAddressModule->set_port(&localWildcard, gAddressModule->get_port(local));
|
||||
AddressModule()->set_to_empty_address(&localWildcard);
|
||||
AddressModule()->set_port(&localWildcard, AddressModule()->get_port(local));
|
||||
|
||||
endpoint = _LookupConnection(&localWildcard, &wildcard);
|
||||
if (endpoint != NULL) {
|
||||
@@ -217,12 +218,12 @@ EndpointManager::Bind(TCPEndpoint *endpoint)
|
||||
sockaddr *address = (sockaddr *)&endpoint->socket->address;
|
||||
|
||||
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;
|
||||
|
||||
uint16 port = gAddressModule->get_port(address);
|
||||
uint16 port = AddressModule()->get_port(address);
|
||||
|
||||
// TODO: check the root group instead?
|
||||
if (ntohs(port) <= kLastReservedPort && geteuid() != 0)
|
||||
@@ -245,7 +246,7 @@ EndpointManager::Bind(TCPEndpoint *endpoint)
|
||||
TCPEndpoint *last = first;
|
||||
while (true) {
|
||||
// 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
|
||||
// wildcard endpoint last
|
||||
return B_PERMISSION_DENIED;
|
||||
@@ -293,9 +294,9 @@ EndpointManager::BindToEphemeral(TCPEndpoint *endpoint)
|
||||
TCPEndpoint *other = _LookupEndpoint(port);
|
||||
if (other == NULL) {
|
||||
// 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,
|
||||
AddressString(gDomain, (sockaddr *)&endpoint->socket->address, true).Data()));
|
||||
AddressString(Domain(), (sockaddr *)&endpoint->socket->address, true).Data()));
|
||||
endpoint->fEndpointNextWithSamePort = NULL;
|
||||
hash_insert(fEndpointHash, endpoint);
|
||||
hash_insert(fConnectionHash, endpoint);
|
||||
@@ -320,7 +321,7 @@ EndpointManager::Unbind(TCPEndpoint *endpoint)
|
||||
RecursiveLocker locker(&fLock);
|
||||
|
||||
if (!endpoint->fSpawned) {
|
||||
TCPEndpoint *other = _LookupEndpoint(gAddressModule->get_port(
|
||||
TCPEndpoint *other = _LookupEndpoint(AddressModule()->get_port(
|
||||
(sockaddr *)&endpoint->socket->address));
|
||||
if (other != endpoint) {
|
||||
// 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
|
||||
|
||||
|
||||
@@ -360,9 +400,9 @@ EndpointManager::_ConnectionCompare(void *_endpoint, const void *_key)
|
||||
const connection_key *key = (connection_key *)_key;
|
||||
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)
|
||||
&& gAddressModule->equal_addresses_and_ports(key->peer,
|
||||
&& key->address_module->equal_addresses_and_ports(key->peer,
|
||||
(sockaddr *)&endpoint->socket->peer))
|
||||
return 0;
|
||||
|
||||
@@ -373,20 +413,23 @@ EndpointManager::_ConnectionCompare(void *_endpoint, const void *_key)
|
||||
/*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 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;
|
||||
TCPEndpoint *endpoint = (TCPEndpoint *)_endpoint;
|
||||
|
||||
return gAddressModule->get_port((sockaddr *)&endpoint->socket->address)
|
||||
== key->port ? 0 : 1;
|
||||
return endpoint->AddressModule()->get_port(
|
||||
(sockaddr *)&endpoint->socket->address) == key->port ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -406,7 +449,8 @@ EndpointManager::_EndpointHash(void *_endpoint, const void *_key, uint32 range)
|
||||
{
|
||||
if (_endpoint != NULL) {
|
||||
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;
|
||||
|
||||
@@ -8,18 +8,24 @@
|
||||
#ifndef ENDPOINT_MANAGER_H
|
||||
#define ENDPOINT_MANAGER_H
|
||||
|
||||
#include "tcp.h"
|
||||
|
||||
#include <net_datalink.h>
|
||||
|
||||
#include <lock.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <util/khash.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
|
||||
struct net_address_module_info;
|
||||
struct net_domain;
|
||||
class TCPEndpoint;
|
||||
|
||||
class EndpointManager {
|
||||
class EndpointManager : public DoublyLinkedListLinkImpl<EndpointManager> {
|
||||
public:
|
||||
EndpointManager();
|
||||
EndpointManager(net_domain *domain);
|
||||
~EndpointManager();
|
||||
|
||||
status_t InitCheck() const;
|
||||
@@ -34,6 +40,13 @@ class EndpointManager {
|
||||
status_t BindToEphemeral(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:
|
||||
TCPEndpoint *_LookupConnection(sockaddr *local, sockaddr *peer);
|
||||
status_t _RemoveConnection(TCPEndpoint *endpoint);
|
||||
@@ -45,6 +58,8 @@ class EndpointManager {
|
||||
static int _EndpointCompare(void *_endpoint, const void *_key);
|
||||
static uint32 _EndpointHash(void *_endpoint, const void *_key, uint32 range);
|
||||
|
||||
net_domain *fDomain;
|
||||
|
||||
hash_table *fConnectionHash;
|
||||
hash_table *fEndpointHash;
|
||||
recursive_lock fLock;
|
||||
|
||||
@@ -158,6 +158,7 @@ WaitList::Signal()
|
||||
|
||||
TCPEndpoint::TCPEndpoint(net_socket *socket)
|
||||
:
|
||||
fManager(NULL),
|
||||
fReceiveList("tcp receive"),
|
||||
fSendList("tcp send"),
|
||||
fOptions(0),
|
||||
@@ -205,7 +206,10 @@ TCPEndpoint::~TCPEndpoint()
|
||||
gStackModule->cancel_timer(&fDelayedAcknowledgeTimer);
|
||||
gStackModule->cancel_timer(&fTimeWaitTimer);
|
||||
|
||||
gEndpointManager->Unbind(this);
|
||||
if (fManager) {
|
||||
fManager->Unbind(this);
|
||||
return_endpoint_manager(fManager);
|
||||
}
|
||||
|
||||
recursive_lock_destroy(&fLock);
|
||||
}
|
||||
@@ -234,7 +238,14 @@ status_t
|
||||
TCPEndpoint::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;
|
||||
}
|
||||
|
||||
@@ -304,7 +315,7 @@ status_t
|
||||
TCPEndpoint::Connect(const struct sockaddr *address)
|
||||
{
|
||||
TRACE("Connect() on address %s",
|
||||
AddressString(gDomain, address, true).Data());
|
||||
AddressString(Domain(), address, true).Data());
|
||||
|
||||
RecursiveLocker locker(&fLock);
|
||||
|
||||
@@ -319,14 +330,14 @@ TCPEndpoint::Connect(const struct sockaddr *address)
|
||||
// get a net_route if there isn't one
|
||||
// TODO: get a net_route_info instead!
|
||||
if (fRoute == NULL) {
|
||||
fRoute = gDatalinkModule->get_route(gDomain, (sockaddr *)address);
|
||||
fRoute = gDatalinkModule->get_route(Domain(), (sockaddr *)address);
|
||||
TRACE(" Connect(): Using Route %p", fRoute);
|
||||
if (fRoute == NULL)
|
||||
return ENETUNREACH;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (status < B_OK) {
|
||||
TRACE(" Connect(): could not add connection: %s!", strerror(status));
|
||||
@@ -413,7 +424,7 @@ TCPEndpoint::Bind(sockaddr *address)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
TRACE("Bind() on address %s",
|
||||
AddressString(gDomain, address, true).Data());
|
||||
AddressString(Domain(), address, true).Data());
|
||||
|
||||
RecursiveLocker lock(fLock);
|
||||
|
||||
@@ -425,13 +436,13 @@ TCPEndpoint::Bind(sockaddr *address)
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
if (gAddressModule->get_port(address) == 0)
|
||||
status = gEndpointManager->BindToEphemeral(this);
|
||||
if (AddressModule()->get_port(address) == 0)
|
||||
status = fManager->BindToEphemeral(this);
|
||||
else
|
||||
status = gEndpointManager->Bind(this);
|
||||
status = fManager->Bind(this);
|
||||
|
||||
TRACE(" Bind() bound to %s (status %i)",
|
||||
AddressString(gDomain, (sockaddr *)&socket->address, true).Data(),
|
||||
AddressString(Domain(), (sockaddr *)&socket->address, true).Data(),
|
||||
(int)status);
|
||||
|
||||
return status;
|
||||
@@ -444,7 +455,7 @@ TCPEndpoint::Unbind(struct sockaddr *address)
|
||||
TRACE("Unbind()");
|
||||
|
||||
RecursiveLocker lock(fLock);
|
||||
return gEndpointManager->Unbind(this);
|
||||
return fManager->Unbind(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -646,7 +657,15 @@ TCPEndpoint::ReadAvailable()
|
||||
bool
|
||||
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)
|
||||
return DROP;
|
||||
|
||||
gAddressModule->set_to((sockaddr *)&newSocket->address,
|
||||
AddressModule()->set_to((sockaddr *)&newSocket->address,
|
||||
(sockaddr *)&buffer->destination);
|
||||
gAddressModule->set_to((sockaddr *)&newSocket->peer,
|
||||
AddressModule()->set_to((sockaddr *)&newSocket->peer,
|
||||
(sockaddr *)&buffer->source);
|
||||
|
||||
TCPEndpoint *endpoint = (TCPEndpoint *)newSocket->first_protocol;
|
||||
@@ -734,12 +753,12 @@ TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
||||
|
||||
// TODO: proper error handling!
|
||||
|
||||
endpoint->fRoute = gDatalinkModule->get_route(gDomain,
|
||||
endpoint->fRoute = gDatalinkModule->get_route(Domain(),
|
||||
(sockaddr *)&newSocket->peer);
|
||||
if (endpoint->fRoute == NULL)
|
||||
return DROP;
|
||||
|
||||
if (gEndpointManager->SetConnection(endpoint, (sockaddr *)&buffer->destination,
|
||||
if (fManager->SetConnection(endpoint, (sockaddr *)&buffer->destination,
|
||||
(sockaddr *)&buffer->source, NULL) < B_OK)
|
||||
return DROP;
|
||||
|
||||
@@ -1085,8 +1104,8 @@ TCPEndpoint::_SendQueued(bool force)
|
||||
return status;
|
||||
}
|
||||
|
||||
gAddressModule->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address);
|
||||
gAddressModule->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer);
|
||||
AddressModule()->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address);
|
||||
AddressModule()->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer);
|
||||
|
||||
uint32 size = buffer->size;
|
||||
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",
|
||||
segment.flags, buffer, buffer->size,
|
||||
AddressString(gDomain, (sockaddr *)&buffer->source, true).Data(),
|
||||
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data());
|
||||
AddressString(Domain(), (sockaddr *)&buffer->source, 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) {
|
||||
gBufferModule->free(buffer);
|
||||
return status;
|
||||
@@ -1525,6 +1544,6 @@ TCPEndpoint::_TimeWaitTimer(struct net_timer *timer, void *data)
|
||||
if (recursive_lock_lock(&endpoint->Lock()) < B_OK)
|
||||
return;
|
||||
|
||||
gSocketModule->delete_socket(endpoint->socket);
|
||||
endpoint->DeleteSocket();
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,8 @@ class TCPEndpoint : public net_protocol {
|
||||
tcp_state State() const { return fState; }
|
||||
bool IsBound() const;
|
||||
|
||||
void DeleteSocket();
|
||||
|
||||
status_t DelayedAcknowledge();
|
||||
status_t SendAcknowledge();
|
||||
status_t UpdateTimeWait();
|
||||
@@ -72,6 +74,12 @@ class TCPEndpoint : public net_protocol {
|
||||
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:
|
||||
friend class EndpointManager;
|
||||
|
||||
@@ -93,6 +101,8 @@ class TCPEndpoint : public net_protocol {
|
||||
static void _PersistTimer(net_timer *timer, void *data);
|
||||
static void _DelayedAcknowledgeTimer(net_timer *timer, void *data);
|
||||
|
||||
EndpointManager *fManager;
|
||||
|
||||
TCPEndpoint *fConnectionHashNext;
|
||||
TCPEndpoint *fEndpointHashNext;
|
||||
TCPEndpoint *fEndpointNextWithSamePort;
|
||||
|
||||
@@ -42,34 +42,56 @@
|
||||
typedef NetBufferField<uint16, offsetof(tcp_header, checksum)> TCPChecksumField;
|
||||
|
||||
|
||||
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;
|
||||
EndpointManager *gEndpointManager;
|
||||
|
||||
|
||||
status_t
|
||||
set_domain(net_interface *interface = NULL)
|
||||
// TODO we need to think of a better way to do this. It would be
|
||||
// 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) {
|
||||
// domain and address module are not known yet, we copy them from
|
||||
// the buffer's interface (if any):
|
||||
if (interface == NULL || interface->domain == NULL)
|
||||
gDomain = gStackModule->get_domain(AF_INET);
|
||||
else
|
||||
gDomain = interface->domain;
|
||||
|
||||
if (gDomain == NULL) {
|
||||
// this shouldn't occur, of course, but who knows...
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
gAddressModule = gDomain->address_module;
|
||||
EndpointManagerList::Iterator iterator = sEndpointManagers.GetIterator();
|
||||
while (iterator.HasNext()) {
|
||||
EndpointManager *endpointManager = iterator.Next();
|
||||
if (endpointManager->Domain() == domain)
|
||||
return endpointManager;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -138,8 +161,8 @@ add_tcp_header(tcp_segment_header &segment, net_buffer *buffer)
|
||||
|
||||
tcp_header &header = bufferHeader.Data();
|
||||
|
||||
header.source_port = gAddressModule->get_port((sockaddr *)&buffer->source);
|
||||
header.destination_port = gAddressModule->get_port((sockaddr *)&buffer->destination);
|
||||
header.source_port = addressModule->get_port((sockaddr *)&buffer->source);
|
||||
header.destination_port = addressModule->get_port((sockaddr *)&buffer->destination);
|
||||
header.sequence = htonl(segment.sequence);
|
||||
header.acknowledge = (segment.flags & TCP_FLAG_ACKNOWLEDGE)
|
||||
? 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,
|
||||
segment.flags, segment.sequence, segment.acknowledge, segment.advertised_window));
|
||||
|
||||
*TCPChecksumField(buffer) = Checksum::PseudoHeader(gAddressModule,
|
||||
*TCPChecksumField(buffer) = Checksum::PseudoHeader(addressModule,
|
||||
gBufferModule, buffer, IPPROTO_TCP);
|
||||
|
||||
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 *
|
||||
name_for_state(tcp_state state)
|
||||
{
|
||||
@@ -350,9 +335,6 @@ tcp_uninit_protocol(net_protocol *protocol)
|
||||
status_t
|
||||
tcp_open(net_protocol *protocol)
|
||||
{
|
||||
if (gDomain == NULL && set_domain() != B_OK)
|
||||
return B_ERROR;
|
||||
|
||||
return ((TCPEndpoint *)protocol)->Open();
|
||||
}
|
||||
|
||||
@@ -497,9 +479,12 @@ tcp_receive_data(net_buffer *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;
|
||||
|
||||
net_domain *domain = buffer->interface->domain;
|
||||
net_address_module_info *addressModule = domain->address_module;
|
||||
|
||||
NetBufferHeaderReader<tcp_header> bufferHeader(buffer);
|
||||
if (bufferHeader.Status() < B_OK)
|
||||
return bufferHeader.Status();
|
||||
@@ -510,16 +495,17 @@ tcp_receive_data(net_buffer *buffer)
|
||||
if (headerLength < sizeof(tcp_header))
|
||||
return B_BAD_DATA;
|
||||
|
||||
if (Checksum::PseudoHeader(gAddressModule, gBufferModule, buffer,
|
||||
if (Checksum::PseudoHeader(addressModule, gBufferModule, buffer,
|
||||
IPPROTO_TCP) != 0)
|
||||
return B_BAD_DATA;
|
||||
|
||||
gAddressModule->set_port((struct sockaddr *)&buffer->source, header.source_port);
|
||||
gAddressModule->set_port((struct sockaddr *)&buffer->destination, header.destination_port);
|
||||
addressModule->set_port((sockaddr *)&buffer->source, header.source_port);
|
||||
addressModule->set_port((sockaddr *)&buffer->destination,
|
||||
header.destination_port);
|
||||
|
||||
TRACE((" Looking for: peer %s, local %s\n",
|
||||
AddressString(gDomain, (sockaddr *)&buffer->source, true).Data(),
|
||||
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data()));
|
||||
AddressString(domain, (sockaddr *)&buffer->source, true).Data(),
|
||||
AddressString(domain, (sockaddr *)&buffer->destination, true).Data()));
|
||||
//dump_tcp_header(header);
|
||||
//gBufferModule->dump(buffer);
|
||||
|
||||
@@ -538,11 +524,17 @@ tcp_receive_data(net_buffer *buffer)
|
||||
bufferHeader.Remove(headerLength);
|
||||
// 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;
|
||||
|
||||
TCPEndpoint *endpoint = gEndpointManager->FindConnection(
|
||||
(struct sockaddr *)&buffer->destination, (struct sockaddr *)&buffer->source);
|
||||
TCPEndpoint *endpoint = endpointManager->FindConnection(
|
||||
(sockaddr *)&buffer->destination, (sockaddr *)&buffer->source);
|
||||
if (endpoint != NULL) {
|
||||
RecursiveLocker locker(endpoint->Lock());
|
||||
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)
|
||||
endpoint->DelayedAcknowledge();
|
||||
else if (segmentAction & DELETE)
|
||||
gSocketModule->delete_socket(endpoint->socket);
|
||||
endpoint->DeleteSocket();
|
||||
} else if ((segment.flags & TCP_FLAG_RESET) == 0)
|
||||
segmentAction = DROP | RESET;
|
||||
|
||||
if (segmentAction & RESET) {
|
||||
// send reset
|
||||
reply_with_reset(segment, buffer);
|
||||
endpointManager->ReplyWithReset(segment, buffer);
|
||||
}
|
||||
if (segmentAction & DROP)
|
||||
gBufferModule->free(buffer);
|
||||
@@ -611,52 +603,39 @@ tcp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
|
||||
static status_t
|
||||
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)
|
||||
goto err1;
|
||||
return status;
|
||||
|
||||
status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, 0,
|
||||
"network/protocols/tcp/v1",
|
||||
"network/protocols/ipv4/v1",
|
||||
NULL);
|
||||
if (status < B_OK)
|
||||
goto err1;
|
||||
return status;
|
||||
|
||||
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 err1;
|
||||
return status;
|
||||
|
||||
status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP,
|
||||
"network/protocols/tcp/v1");
|
||||
if (status < B_OK)
|
||||
goto err1;
|
||||
return status;
|
||||
|
||||
return B_OK;
|
||||
|
||||
err1:
|
||||
delete gEndpointManager;
|
||||
|
||||
TRACE(("init_tcp() fails with %lx (%s)\n", status, strerror(status)));
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
tcp_uninit()
|
||||
{
|
||||
delete gEndpointManager;
|
||||
benaphore_destroy(&sEndpointManagersLock);
|
||||
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_datalink_module_info *gDatalinkModule;
|
||||
extern net_socket_module_info *gSocketModule;
|
||||
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);
|
||||
|
||||
EndpointManager *create_endpoint_manager(net_domain *domain);
|
||||
void return_endpoint_manager(EndpointManager *);
|
||||
|
||||
#endif // TCP_H
|
||||
|
||||
Reference in New Issue
Block a user