[tahoe-dev] [tahoe-lafs] #867: use ipv6

Randall Mason clashthebunny at gmail.com
Mon Feb 18 13:39:36 UTC 2013


A quick note about this:

> Avoiding mess is always good.  What happens currently with 169.254.0.0/16
> addresses in Tahoe?  What about RFC1918 addresses?  What about 127.0.0.0/8?
> Are they deprioritized so that connections happen to them in the last case?
> What is the delay if the connection times out?  Does Tahoe only connect in
> serial, as apposed to starting to open up x different connections and take
> the first one that connects?  Does Tahoe use the order of the address in the
> furl?  What's the current algorithm for IPv4 addresses and the justification
> for it?

Looking at foolscap, it does start a connection to every single
address in the furl and takes the first one that succeeds.  All the
interesting stuff happens in negotiate.py:

Each hint in the furl has a connection opened to it:

1376     def connectToAll(self):
1377         while self.remainingLocations:
1378             location = self.remainingLocations.pop()
1379             if location in self.attemptedLocations:
1380                 continue
1381             self.attemptedLocations.append(location)
1382             host, port = location
1383             lp = self.log("connectTCP to %s" % (location,))
1384             f = TubConnectorClientFactory(self, host, lp)
1385             c = reactor.connectTCP(host, port, f)
1386             self.pendingConnections[f] = c
1387             # the tcp.Connector that we get back from
reactor.connectTCP will
1388             # retain a reference to the transport that it
creates, so we can
1389             # use it to disconnect the established (but not yet
negotiated)
1390             # connection
1391             if
self.tub.options.get("debug_stall_second_connection"):
1392                 # for unit tests, hold off on making the second
connection
1393                 # for a moment. This allows the first connection
to get to a
1394                 # known state.
1395                 reactor.callLater(0.1, self.connectToAll)
1396                 return
1397         self.checkForFailure()

Once a connection succeeds, it calls negotiationComplete:

1173         # if we were created as a client, we'll have a
TubConnector. Let them
1174         # know that this connection has succeeded, so they can
stop any other
1175         # connection attempts still in progress.
1176         if self.isClient:
1177             self.connector.negotiationComplete(self.factory)

Go through all the other connections and kill them:

1437     def negotiationComplete(self, factory):
1438         # 'factory' has just completed negotiation, so abandon
all the other
1439         # connection attempts
1440         self.log("negotiationComplete, %s won" % factory)
1441         self.active = False
1442         if self.timer:
1443             self.timer.cancel()
1444             self.timer = None
1445         del self.pendingConnections[factory] # this one succeeded
1446         for f in self.pendingConnections.keys(): # abandon the
others
1447             # for connections that are not yet established, this
will trigger
1448             # clientConnectionFailed. For connections that are
established
1449             # (and exchanging negotiation messages), this does
1450             # loseConnection() and will thus trigger
negotiationFailed.
1451             f.disconnect()
1452         self.checkForIdle()

Because of this, it doesn't seem that removing any addresses seems
like a win.  It can only lose.  A simple change within foolscap will
allow for each fe80::/10 address to be connected to with each
interface that's up.  This will result in more connections, but it
will succeed in connecting in side cases.  The change would go in
negotiate.py here:

1316         for h in self.target.getLocations():
1317             if h[0] == "ipv4" or h[0] == "ipv6":
1318                 (host, port) = h[1:]
1319                 hints.append( (host, port) )

which is part of the already proposed changes to foolscap.  If we can
just add a re that finds fe80::/10 addresses and loop over the
interfaces on the host appending each %if to each fe80 address.  This
way all addresses possible are used.  None of the gymnastics of
figuring out what port the fe80:: address is on, just try a connect()
on all interfaces.  This will still take the lowest latency link to
connect (at least on average).  Even without any change, there is not
much of a performance penalty.  connectAll() is only called if there
is a "redirect" and on initial startup.  It does not jump around with
connections once it has a good link, so it should be the best
performance too.  It does not prioritize either the order or the
protocol, even though I list ipv6 addresses first in the furl, I end
up with an ipv4 connection much of the time.  I've also ended up with
different ones of my universally addressable IPv6 addresses too when
only on IPv6.

-Randall



More information about the tahoe-dev mailing list