Commit 620b9e98 authored by Julien Muchembled's avatar Julien Muchembled

Fix use of alternate addresses

When a peer advertised several addresses, a node trying to create a tunnel to
it never tried any other address than the first one.

Before, we wrongly assumed OpenVPN would try all addresses before aborting
(--ping-exit). New code reexecutes OpenVPN until all addresses are tried
and update the peer db to reorder addresses if the first one failed.
parent 94e8a309
...@@ -95,17 +95,19 @@ class PeerDB(object): ...@@ -95,17 +95,19 @@ class PeerDB(object):
return bootpeer return bootpeer
logging.warning('Buggy registry sent us our own address') logging.warning('Buggy registry sent us our own address')
def addPeer(self, prefix, address): def addPeer(self, prefix, address, force=False):
logging.debug('Adding peer %s: %s', prefix, address) logging.debug('Adding peer %s: %s', prefix, address)
with self._db: with self._db:
q = self._db.execute q = self._db.execute
try: try:
(a,), = q("SELECT address FROM peer WHERE prefix=?", (prefix,)) (a,), = q("SELECT address FROM peer WHERE prefix=?", (prefix,))
a = a != address if force else \
set(a.split(';')) != set(address.split(';'))
except ValueError: except ValueError:
q("DELETE FROM peer WHERE prefix IN (SELECT peer" q("DELETE FROM peer WHERE prefix IN (SELECT peer"
" FROM volatile.stat ORDER BY try, RANDOM() LIMIT ?,-1)", " FROM volatile.stat ORDER BY try, RANDOM() LIMIT ?,-1)",
(self._db_size,)) (self._db_size,))
a = None a = True
if a != address: if a:
q("INSERT OR REPLACE INTO peer VALUES (?,?)", (prefix, address)) q("INSERT OR REPLACE INTO peer VALUES (?,?)", (prefix, address))
q("INSERT OR REPLACE INTO volatile.stat VALUES (?,0)", (prefix,)) q("INSERT OR REPLACE INTO volatile.stat VALUES (?,0)", (prefix,))
...@@ -58,14 +58,30 @@ class Connection(object): ...@@ -58,14 +58,30 @@ class Connection(object):
self._remote_ip_set.add(ip) self._remote_ip_set.add(ip)
return iter(self._remote_ip_set) return iter(self._remote_ip_set)
def open(self, write_pipe, timeout, encrypt, ovpn_args): def open(self, write_pipe, timeout, encrypt, ovpn_args, _retry=0):
self.process = plib.client(self.iface, self.address_list, encrypt, self.process = plib.client(
self.iface, (self.address_list[_retry],), encrypt,
'--tls-remote', '%u/%u' % (int(self._prefix, 2), len(self._prefix)), '--tls-remote', '%u/%u' % (int(self._prefix, 2), len(self._prefix)),
'--resolv-retry', '0', '--resolv-retry', '0',
'--connect-retry-max', '3', '--tls-exit', '--connect-retry-max', '3', '--tls-exit',
'--ping-exit', str(timeout), '--ping-exit', str(timeout),
'--route-up', '%s %u' % (plib.ovpn_client, write_pipe), '--route-up', '%s %u' % (plib.ovpn_client, write_pipe),
*ovpn_args) *ovpn_args)
_retry += 1
self._retry = _retry < len(self.address_list) and (
write_pipe, timeout, encrypt, ovpn_args, _retry)
def connected(self, db):
try:
i = self._retry[-1] - 1
self._retry = None
except TypeError:
i = len(self.address_list) - 1
if i:
db.addPeer(self._prefix, utils.dump_address(
self.address_list[i:] + self.address_list[:i]), True)
else:
db.connecting(self._prefix, 0)
def close(self): def close(self):
try: try:
...@@ -78,7 +94,11 @@ class Connection(object): ...@@ -78,7 +94,11 @@ class Connection(object):
if self.process.poll() != None: if self.process.poll() != None:
logging.info('Connection with %s has failed with return code %s', logging.info('Connection with %s has failed with return code %s',
self._prefix, self.process.returncode) self._prefix, self.process.returncode)
return False if not self._retry:
return False
logging.info('Retrying with alternate address')
self.close()
self.open(*self._retry)
return True return True
...@@ -367,7 +387,11 @@ class TunnelManager(object): ...@@ -367,7 +387,11 @@ class TunnelManager(object):
self._gateway_manager.remove(trusted_ip) self._gateway_manager.remove(trusted_ip)
def _ovpn_route_up(self, common_name, ip): def _ovpn_route_up(self, common_name, ip):
self._peer_db.connecting(utils.binFromSubnet(common_name), 0) prefix = utils.binFromSubnet(common_name)
try:
self._connection_dict[prefix].connected(self._peer_db)
except KeyError:
pass
if self._ip_changed: if self._ip_changed:
self._address = utils.dump_address(self._ip_changed(ip)) self._address = utils.dump_address(self._ip_changed(ip))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment