* _BindToEphemeral() now remembers the last port it used, and tries a higher

one next. This should work around the problem Ingo observed with the OpenSSH
  tests (not tested, though).
* The only TCP compliant fix for this problem would be to not enter time wait
  for local connections at all (which I will do soon).
* Another work around other implementations use is to accept incoming
  connections for time wait endpoints (the connection will then be replaced).
  I'm not yet sure if I want that behaviour, though.
* Added tracing for unbinding endpoints (with stack trace) to investigate
  another bug (sometimes closed connections aren't actually closed).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25218 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2008-04-27 18:14:03 +00:00
parent c96512dab1
commit 42759f5626
2 changed files with 73 additions and 16 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2006-2007, Haiku, Inc. All Rights Reserved. * Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -10,12 +10,14 @@
#include "EndpointManager.h" #include "EndpointManager.h"
#include <new>
#include <unistd.h> #include <unistd.h>
#include <KernelExport.h> #include <KernelExport.h>
#include <NetUtilities.h> #include <NetUtilities.h>
#include <util/AutoLock.h> #include <util/AutoLock.h>
#include <tracing.h>
#include "TCPEndpoint.h" #include "TCPEndpoint.h"
@@ -27,6 +29,49 @@
# define TRACE(x) # define TRACE(x)
#endif #endif
//#define ENDPOINT_TRACING
#ifdef ENDPOINT_TRACING
namespace EndpointTracing {
class Unbind : public AbstractTraceEntry {
public:
Unbind(TCPEndpoint* endpoint)
:
fEndpoint(endpoint)
{
fStackTrace = capture_tracing_stack_trace(10, 0, false);
endpoint->LocalAddress().AsString(fLocal, sizeof(fLocal), true);
endpoint->PeerAddress().AsString(fPeer, sizeof(fPeer), true);
Initialized();
}
virtual void DumpStackTrace(TraceOutput& out)
{
out.PrintStackTrace(fStackTrace);
}
virtual void AddDump(TraceOutput& out)
{
out.Print("tcp:e:unbind: %p, local %s, peer %s"
"%lu", fEndpoint, fLocal, fPeer);
}
protected:
TCPEndpoint* fEndpoint;
tracing_stack_trace* fStackTrace;
char fLocal[32];
char fPeer[32];
};
} // namespace EndpointTracing
# define T(x) new(std::nothrow) EndpointTracing::x
#else
# define T(x)
#endif // ENDPOINT_TRACING
static const uint16 kLastReservedPort = 1023; static const uint16 kLastReservedPort = 1023;
static const uint16 kFirstEphemeralPort = 40000; static const uint16 kFirstEphemeralPort = 40000;
@@ -70,6 +115,9 @@ ConnectionHashDefinition::GetLink(TCPEndpoint *endpoint) const
} }
// #pragma mark -
size_t size_t
EndpointHashDefinition::HashKey(uint16 port) const EndpointHashDefinition::HashKey(uint16 port) const
{ {
@@ -106,8 +154,14 @@ EndpointHashDefinition::GetLink(TCPEndpoint *endpoint) const
} }
// #pragma mark -
EndpointManager::EndpointManager(net_domain *domain) EndpointManager::EndpointManager(net_domain *domain)
: fDomain(domain), fConnectionHash(this) :
fDomain(domain),
fConnectionHash(this),
fLastPort(kFirstEphemeralPort)
{ {
benaphore_init(&fLock, "endpoint manager"); benaphore_init(&fLock, "endpoint manager");
} }
@@ -138,8 +192,7 @@ EndpointManager::InitCheck() const
// #pragma mark - connections // #pragma mark - connections
/*! /*! Returns the endpoint matching the connection.
Returns the endpoint matching the connection.
You must hold the manager's lock when calling this method. You must hold the manager's lock when calling this method.
*/ */
TCPEndpoint * TCPEndpoint *
@@ -236,7 +289,7 @@ EndpointManager::FindConnection(sockaddr *local, sockaddr *peer)
return endpoint; return endpoint;
} }
// no matching endpoint exists // no matching endpoint exists
TRACE(("TCP: no matching endpoint!\n")); TRACE(("TCP: no matching endpoint!\n"));
return NULL; return NULL;
@@ -310,28 +363,30 @@ EndpointManager::_BindToEphemeral(TCPEndpoint *endpoint,
{ {
TRACE(("EndpointManager::BindToEphemeral(%p)\n", endpoint)); TRACE(("EndpointManager::BindToEphemeral(%p)\n", endpoint));
uint32 max = kFirstEphemeralPort + 65536; uint32 max = fLastPort + 65536;
for (int32 i = 1; i < 5; i++) { for (int32 i = 1; i < 5; i++) {
// try to retrieve a more or less random port // try to retrieve a more or less random port
uint32 counter = kFirstEphemeralPort;
uint32 step = i == 4 ? 1 : (system_time() & 0x1f) + 1; uint32 step = i == 4 ? 1 : (system_time() & 0x1f) + 1;
uint32 counter = fLastPort + step;
while (counter < max) { while (counter < max) {
uint16 port = counter & 0xffff; uint16 port = counter & 0xffff;
if (port <= kLastReservedPort) if (port <= kLastReservedPort)
port += kLastReservedPort; port += kLastReservedPort;
fLastPort = port;
port = htons(port); port = htons(port);
if (!fEndpointHash.Lookup(port).HasNext()) { if (!fEndpointHash.Lookup(port).HasNext()) {
// found a port
SocketAddressStorage newAddress(AddressModule()); SocketAddressStorage newAddress(AddressModule());
newAddress.SetTo(address); newAddress.SetTo(address);
newAddress.SetPort(port); newAddress.SetPort(port);
// found a port TRACE((" EndpointManager::BindToEphemeral(%p) -> %s\n",
TRACE((" EndpointManager::BindToEphemeral(%p) -> %s\n", endpoint, endpoint, AddressString(Domain(), *newAddress,
AddressString(Domain(), *newAddress, true).Data())); true).Data()));
return _Bind(endpoint, *newAddress); return _Bind(endpoint, *newAddress);
} }
@@ -364,6 +419,7 @@ status_t
EndpointManager::Unbind(TCPEndpoint *endpoint) EndpointManager::Unbind(TCPEndpoint *endpoint)
{ {
TRACE(("EndpointManager::Unbind(%p)\n", endpoint)); TRACE(("EndpointManager::Unbind(%p)\n", endpoint));
T(Unbind(endpoint));
if (endpoint == NULL || !endpoint->IsBound()) { if (endpoint == NULL || !endpoint->IsBound()) {
TRACE((" endpoint is unbound.\n")); TRACE((" endpoint is unbound.\n"));
@@ -429,10 +485,10 @@ EndpointManager::DumpEndpoints() const
kprintf("%10s %20s %20s %8s %8s %12s\n", "address", "local", "peer", kprintf("%10s %20s %20s %8s %8s %12s\n", "address", "local", "peer",
"recv-q", "send-q", "state"); "recv-q", "send-q", "state");
ConnectionTable::Iterator it = fConnectionHash.GetIterator(); ConnectionTable::Iterator iterator = fConnectionHash.GetIterator();
while (it.HasNext()) { while (iterator.HasNext()) {
TCPEndpoint *endpoint = it.Next(); TCPEndpoint *endpoint = iterator.Next();
char localBuf[64], peerBuf[64]; char localBuf[64], peerBuf[64];
endpoint->LocalAddress().AsString(localBuf, sizeof(localBuf), true); endpoint->LocalAddress().AsString(localBuf, sizeof(localBuf), true);
@@ -101,9 +101,10 @@ class EndpointManager : public DoublyLinkedListLinkImpl<EndpointManager> {
typedef OpenHashTable<ConnectionHashDefinition> ConnectionTable; typedef OpenHashTable<ConnectionHashDefinition> ConnectionTable;
typedef MultiHashTable<EndpointHashDefinition> EndpointTable; typedef MultiHashTable<EndpointHashDefinition> EndpointTable;
ConnectionTable fConnectionHash; ConnectionTable fConnectionHash;
EndpointTable fEndpointHash; EndpointTable fEndpointHash;
benaphore fLock; uint16 fLastPort;
benaphore fLock;
}; };
#endif // ENDPOINT_MANAGER_H #endif // ENDPOINT_MANAGER_H