Commit 849e2929 authored by Ulysse Beaugnon's avatar Ulysse Beaugnon

changes in upnpigd to be able to refresh the forwarding

changes in tunnel.py to falg the dead peers
changes in db.py to remove old peers when necessary
parent 9e65bddd
To be done : To be done :
The address of the client is declared while it should only be the address
of the server
Upgrade the logging function in order to be able to log message like Upgrade the logging function in order to be able to log message like
"Refreshing peers DB ... done", or add log messages to specify that an "Refreshing peers DB ... done", or add log messages to specify that an
action advertised by a previous log message has been completed action advertised by a previous log message has been completed
...@@ -8,20 +11,15 @@ To be done : ...@@ -8,20 +11,15 @@ To be done :
Use an algorithm to choose which connections to keep and/or establish Use an algorithm to choose which connections to keep and/or establish
instead of pure randomness instead of pure randomness
|-> number of routes / tunnel : doesn't work |-> number of routes / tunnel
|-> favorise most used roads ? |-> favorise most used roads ?
Replace comments at the beginning of functions with docstrings & give all Replace comments at the beginning of functions with docstrings & give all
fn docstrings fn docstrings
In peers DB, flag the dead peers so we only choose them if necessary and we
can remove them if we have enought peers
Use a timeout for the server peersDB so we can flag unreachable peers and Use a timeout for the server peersDB so we can flag unreachable peers and
remove the peers whose certificate is no longer valid remove the peers whose certificate is no longer valid
Specify a lease duration in ForwardViaUPnP and also forward a tcp port
Handle LAN internally in order not to have catastrophic results .... Handle LAN internally in order not to have catastrophic results ....
( avahi could be used ) ( avahi could be used )
...@@ -51,14 +49,5 @@ To be discussed: ...@@ -51,14 +49,5 @@ To be discussed:
rapport rapport
==> It takes babel between 3 times and 4 times the hello interval to ==> It takes babel between 3 times and 4 times the hello interval to
reestablish connection, if a direct link is cut reestablish connection, if a direct link is cut
U : So we have to reduce the hello interval. 2min to detect a dead link is
G : I think the number of route going through an interface should be a far too much.
Connection attribute, not a dict in tunnelManager
U : Yes, it was planned, just wait for me to finish implementing it
U : '--up', 'ovpn-server %s/%u' % (server_ip, len(network)) in plib.py
if you use len(network), this means that all our network is on the
same LAN and that the interface of the server is connected to it
wich means that any packet should be routed to this interface
an interface should only advertise the /64 (or less) which has been
attributed to it
import sqlite3, socket, xmlrpclib, time, os import sqlite3, socket, xmlrpclib, time, os
import utils import utils
class PeerManager: class PeerManager:
# internal ip = temp arg/attribute # internal ip = temp arg/attribute
...@@ -16,7 +17,8 @@ class PeerManager: ...@@ -16,7 +17,8 @@ class PeerManager:
self._proto = proto self._proto = proto
self._manual = manual self._manual = manual
self._proxy = xmlrpclib.ServerProxy('http://%s:%u' % (server, server_port)) self._proxy = xmlrpclib.ServerProxy('http://%s:%u'
% (server, server_port))
utils.log('Connectiong to peers database', 4) utils.log('Connectiong to peers database', 4)
self._db = sqlite3.connect(os.path.join(db_dir_path, 'peers.db'), self._db = sqlite3.connect(os.path.join(db_dir_path, 'peers.db'),
...@@ -44,32 +46,43 @@ class PeerManager: ...@@ -44,32 +46,43 @@ class PeerManager:
def _declare(self): def _declare(self):
if self._address != None: if self._address != None:
utils.log('Sending connection info to server', 3) utils.log('Sending connection info to server', 3)
self._proxy.declare((self._internal_ip, utils.address_list(self._address))) self._proxy.declare((self._internal_ip,
utils.address_list(self._address)))
else: else:
utils.log("Warning : couldn't advertise to server, external config not known", 4) utils.log("Warning : couldn't send ip, unknown external config", 4)
def _populate(self): def _populate(self):
utils.log('Populating the peers DB', 2) utils.log('Populating the peers DB', 2)
new_peer_list = self._proxy.getPeerList(self._db_size, self._internal_ip) new_peer_list = self._proxy.getPeerList(self._db_size,
self._db.executemany("INSERT OR IGNORE INTO peers (prefix, address) VALUES (?,?)", new_peer_list) self._internal_ip)
self._db.execute("""DELETE FROM peers WHERE used <= 0 ORDER BY used,
RANDOM() LIMIT MAX(0, ? + (SELECT COUNT(*)
FROM peers WHERE used <= 0))""",
(str(len(new_peer_list) - self._db_size),))
self._db.executemany("""INSERT OR IGNORE INTO peers (prefix, address)
VALUES (?,?)""", new_peer_list)
self._db.execute("DELETE FROM peers WHERE prefix = ?", (self._prefix,)) self._db.execute("DELETE FROM peers WHERE prefix = ?", (self._prefix,))
utils.log('New peers : %s' % ', '.join(map(str, new_peer_list)), 5) utils.log('New peers : %s' % ', '.join(map(str, new_peer_list)), 5)
def getUnusedPeers(self, peer_count): def getUnusedPeers(self, peer_count):
return self._db.execute("""SELECT prefix, address FROM peers WHERE used <= 0 return self._db.execute("""SELECT prefix, address FROM peers WHERE used
ORDER BY used DESC,RANDOM() LIMIT ?""", (peer_count,)) <= 0 ORDER BY used DESC,RANDOM() LIMIT ?""",
(peer_count,))
def usePeer(self, prefix): def usePeer(self, prefix):
utils.log('Updating peers database : using peer ' + str(prefix), 5) utils.log('Updating peers database : using peer ' + str(prefix), 5)
self._db.execute("UPDATE peers SET used = 1 WHERE prefix = ?", (prefix,)) self._db.execute("UPDATE peers SET used = 1 WHERE prefix = ?",
(prefix,))
def unusePeer(self, prefix): def unusePeer(self, prefix):
utils.log('Updating peers database : unusing peer ' + str(prefix), 5) utils.log('Updating peers database : unusing peer ' + str(prefix), 5)
self._db.execute("UPDATE peers SET used = 0 WHERE prefix = ?", (prefix,)) self._db.execute("UPDATE peers SET used = 0 WHERE prefix = ?",
(prefix,))
def flagPeer(self, prefix): def flagPeer(self, prefix):
utils.log('Updating peers database : flagging peer ' + str(prefix), 5) utils.log('Updating peers database : flagging peer ' + str(prefix), 5)
self._db.execute("UPDATE peers SET used = -1 WHERE prefix = ?", (prefix,)) self._db.execute("UPDATE peers SET used = -1 WHERE prefix = ?",
(prefix,))
def handle_message(self, msg): def handle_message(self, msg):
script_type, arg = msg.split() script_type, arg = msg.split()
...@@ -84,7 +97,9 @@ class PeerManager: ...@@ -84,7 +97,9 @@ class PeerManager:
for proto in self._proto) for proto in self._proto)
if self._address != new_address: if self._address != new_address:
self._address = new_address self._address = new_address
utils.log('Received new external configuration : %s:%s' % (external_ip, external_port), 3) utils.log('Received new external configuration : %s:%s'
% (external_ip, external_port), 3)
self._declare() self._declare()
else: else:
utils.log('Unknow message recieved from the openvpn pipe : ' + msg, 1) utils.log('Unknow message recieved from the openvpn pipe : '
+ msg, 1)
...@@ -4,6 +4,7 @@ import plib, utils, db ...@@ -4,6 +4,7 @@ import plib, utils, db
log = None log = None
smooth = 0.3 smooth = 0.3
class Connection: class Connection:
def __init__(self, address, write_pipe, hello, iface, prefix, def __init__(self, address, write_pipe, hello, iface, prefix,
...@@ -11,7 +12,7 @@ class Connection: ...@@ -11,7 +12,7 @@ class Connection:
self.process = plib.client(address, write_pipe, hello, '--dev', iface, self.process = plib.client(address, write_pipe, hello, '--dev', iface,
*ovpn_args, stdout=os.open(os.path.join(log, *ovpn_args, stdout=os.open(os.path.join(log,
'vifibnet.client.%s.log' % (prefix,)), 'vifibnet.client.%s.log' % (prefix,)),
os.O_WRONLY|os.O_CREAT|os.O_TRUNC)) os.O_WRONLY | os.O_CREAT | os.O_TRUNC))
self.iface = iface self.iface = iface
self.routes = 0 self.routes = 0
...@@ -24,7 +25,7 @@ class Connection: ...@@ -24,7 +25,7 @@ class Connection:
def refresh(self): def refresh(self):
# Check that the connection is alive # Check that the connection is alive
if self.process.poll() != None: if self.process.poll() != None:
utils.log('Connection with %s has failed with return code %s' utils.log('Connection with %s has failed with return code %s'
% (self._prefix, self.process.returncode), 3) % (self._prefix, self.process.returncode), 3)
return False return False
...@@ -42,22 +43,24 @@ class Connection: ...@@ -42,22 +43,24 @@ class Connection:
t = time.time() t = time.time()
if bool(self._last_trafic): if bool(self._last_trafic):
bw = (trafic - self._last_trafic)/(t - bw = (trafic - self._last_trafic) / (t -
self._last_trafic_update) self._last_trafic_update)
if bool(self._bandwidth): if bool(self._bandwidth):
self._bandwidth = (1-smooth)*self._bandwidth + smooth*bw self._bandwidth = ((1 - smooth) * self._bandwidth
+ smooth * bw)
else: else:
self._bandwidth = bw self._bandwidth = bw
utils.log('New bandwidth calculated on iface %s : %s' % utils.log('New bandwidth calculated on iface %s : %s' %
(self.iface, self._bandwidth), 4) (self.iface, self._bandwidth), 4)
self._last_trafic_update = t self._last_trafic_update = t
self._last_trafic = trafic self._last_trafic = trafic
except IOError: # This just means that the interface is downs except IOError: # This just means that the interface is downs
utils.log('Unable to calculate bandwidth on iface %s' % utils.log('Unable to calculate bandwidth on iface %s' %
self.iface, 4) self.iface, 4)
class TunnelManager: class TunnelManager:
def __init__(self, write_pipe, peer_db, openvpn_args, hello_interval, def __init__(self, write_pipe, peer_db, openvpn_args, hello_interval,
...@@ -75,8 +78,8 @@ class TunnelManager: ...@@ -75,8 +78,8 @@ class TunnelManager:
'client10', 'client11', 'client12')) 'client10', 'client11', 'client12'))
self.next_refresh = time.time() self.next_refresh = time.time()
self._client_count = connection_count/2 self._client_count = connection_count / 2
self._refresh_count = refresh_rate*self._client_count self._refresh_count = refresh_rate * self._client_count
def refresh(self): def refresh(self):
utils.log('Refreshing the tunnels', 2) utils.log('Refreshing the tunnels', 2)
...@@ -88,9 +91,9 @@ class TunnelManager: ...@@ -88,9 +91,9 @@ class TunnelManager:
def _cleanDeads(self): def _cleanDeads(self):
for prefix in self._connection_dict.keys(): for prefix in self._connection_dict.keys():
if not self._connection_dict[prefix].refresh(): if not self._connection_dict[prefix].refresh():
self._kill(prefix) self._kill(prefix)
self._peer_db.flagPeer(prefix)
def _removeSomeTunnels(self): def _removeSomeTunnels(self):
for i in range(0, max(0, len(self._connection_dict) - for i in range(0, max(0, len(self._connection_dict) -
...@@ -135,14 +138,12 @@ class TunnelManager: ...@@ -135,14 +138,12 @@ class TunnelManager:
self._connection_dict[self._iface_to_prefix[iface]].routes = 0 self._connection_dict[self._iface_to_prefix[iface]].routes = 0
f = open('/proc/net/ipv6_route', 'r') f = open('/proc/net/ipv6_route', 'r')
for line in f: for line in f:
ip, subnet_size, iface = struct.unpack("""32s x 2s 106x ip, subnet_size, iface = struct.unpack('32s x 2s 106x %ss x'
%ss x""" % (len(line)-142), line) % (len(line) - 142), line)
iface = iface.replace(' ', '') iface = iface.replace(' ', '')
if iface in self._iface_to_prefix.keys(): if iface in self._iface_to_prefix.keys():
self._connection_dict[self._iface_to_prefix[iface]].routes += 1 self._connection_dict[self._iface_to_prefix[iface]].routes += 1
for p in self._connection_dict.keys(): for p in self._connection_dict.keys():
utils.log('Routes on iface %s : %s' % ( utils.log('Routes on iface %s : %s' % (
self._connection_dict[p].iface, self._connection_dict[p].iface,
self._connection_dict[p].routes ), 5) self._connection_dict[p].routes), 5)
import miniupnpc import miniupnpc
import socket import time
import utils
# return (address, port)
def ForwardViaUPnP(local_port, protos):
u = miniupnpc.UPnP()
u.discoverdelay = 200
u.discover()
u.selectigd()
external_port = 1000
while True: class UpnpForward:
def __init__(self, local_port, protos):
self._u = miniupnpc.UPnP()
self._u.discoverdelay = 200
self.external_port = 1000
self._local_port = local_port
self._protos = protos
self._u.discover()
self._u.selectigd()
if 'udp' in protos: if 'udp' in protos:
while u.getspecificportmapping(external_port, 'UDP') != None : while self._u.getspecificportmapping(self.external_port,
external_port += 1 'UDP') != None:
if external_port == 65536: self.external_port += 1
if self.external_port == 65536:
raise Exception raise Exception
if 'tcp-server' in protos: if 'tcp-server' in protos:
while u.getspecificportmapping(external_port, 'TCP') != None : while self._u.getspecificportmapping(self.external_port,
external_port += 1 'TCP') != None:
if external_port == 65536: self.external_port += 1
if self._external_port == 65536:
raise Exception raise Exception
if 'udp' in protos: if 'udp' in protos:
u.addportmapping(external_port, 'UDP', u.lanaddr, local_port, self._u.addportmapping(self.external_port, 'UDP',
'Vifib openvpn server', '') self._u.lanaddr, local_port, 'Vifib openvpn server', '')
if 'tcp-server' in protos: if 'tcp-server' in protos:
u.addportmapping(external_port, 'TCP', u.lanaddr, local_port, self._u.addportmapping(self.external_port, 'TCP',
'Vifib openvpn server', '') self._u.lanaddr, local_port, 'Vifib openvpn server', '')
print (u.externalipaddress(), external_port) self.external_ip = self._u.externalipaddress()
return (u.externalipaddress(), external_port) utils.log('Forwarding %s:%s to %s:%s' % (self.external_ip,
self.external_port, self._u.lanaddr, local_port), 3)
self.next_refresh = time.time() + 3600
def Refresh(self):
if 'udp' in self._protos:
self._u.addportmapping(self.external_port, 'UDP', self._u.lanaddr,
self._local_port, 'Vifib openvpn server', '')
if 'tcp-server' in self._protos:
self._u.addportmapping(self.external_port, 'TCP', self._u.lanaddr,
self._local_port, 'Vifib openvpn server', '')
self.next_refresh = time.time() + 3600
...@@ -3,6 +3,7 @@ import argparse, errno, os, select, subprocess, time ...@@ -3,6 +3,7 @@ import argparse, errno, os, select, subprocess, time
from argparse import ArgumentParser from argparse import ArgumentParser
import db, plib, upnpigd, utils, tunnel import db, plib, upnpigd, utils, tunnel
class ArgParser(ArgumentParser): class ArgParser(ArgumentParser):
def convert_arg_line_to_args(self, arg_line): def convert_arg_line_to_args(self, arg_line):
...@@ -12,6 +13,7 @@ class ArgParser(ArgumentParser): ...@@ -12,6 +13,7 @@ class ArgParser(ArgumentParser):
if arg.strip(): if arg.strip():
yield arg yield arg
def ovpnArgs(optional_args, ca_path, cert_path): def ovpnArgs(optional_args, ca_path, cert_path):
# Treat openvpn arguments # Treat openvpn arguments
if optional_args[0] == "--": if optional_args[0] == "--":
...@@ -22,6 +24,7 @@ def ovpnArgs(optional_args, ca_path, cert_path): ...@@ -22,6 +24,7 @@ def ovpnArgs(optional_args, ca_path, cert_path):
optional_args.append(cert_path) optional_args.append(cert_path)
return optional_args return optional_args
def getConfig(): def getConfig():
parser = ArgParser(fromfile_prefix_chars='@', parser = ArgParser(fromfile_prefix_chars='@',
description='Resilient virtual private network application') description='Resilient virtual private network application')
...@@ -78,6 +81,7 @@ def getConfig(): ...@@ -78,6 +81,7 @@ def getConfig():
help="Common OpenVPN options (e.g. certificates)") help="Common OpenVPN options (e.g. certificates)")
return parser.parse_args() return parser.parse_args()
def main(): def main():
# Get arguments # Get arguments
config = getConfig() config = getConfig()
...@@ -101,10 +105,11 @@ def main(): ...@@ -101,10 +105,11 @@ def main():
else: else:
utils.log('Attempting automatic configuration via UPnP', 4) utils.log('Attempting automatic configuration via UPnP', 4)
try: try:
ext_ip, ext_port = upnpigd.ForwardViaUPnP(config.internal_port, config.proto) forward = upnpigd.UpnpForward(config.internal_port, config.proto)
config.address = list([ext_ip, str(ext_port), proto] config.address = list([forward.external_ip,
for proto in config.proto) str(forward.external_port), proto] for proto in config.proto)
except Exception: except Exception:
forward = None
utils.log('An atempt to forward a port via UPnP failed', 4) utils.log('An atempt to forward a port via UPnP failed', 4)
peer_db = db.PeerManager(config.state, config.server, config.server_port, peer_db = db.PeerManager(config.state, config.server, config.server_port,
...@@ -119,7 +124,7 @@ def main(): ...@@ -119,7 +124,7 @@ def main():
router = plib.router(network, internal_ip, interface_list, config.wireless, router = plib.router(network, internal_ip, interface_list, config.wireless,
config.hello, os.path.join(config.state, 'vifibnet.babeld.state'), config.hello, os.path.join(config.state, 'vifibnet.babeld.state'),
stdout=os.open(os.path.join(config.log, 'vifibnet.babeld.log'), stdout=os.open(os.path.join(config.log, 'vifibnet.babeld.log'),
os.O_WRONLY|os.O_CREAT|os.O_TRUNC), stderr=subprocess.STDOUT) os.O_WRONLY | os.O_CREAT | os.O_TRUNC), stderr=subprocess.STDOUT)
# Establish connections # Establish connections
server_process = list(plib.server(internal_ip, len(network) + len(prefix), server_process = list(plib.server(internal_ip, len(network) + len(prefix),
...@@ -133,9 +138,12 @@ def main(): ...@@ -133,9 +138,12 @@ def main():
# main loop # main loop
try: try:
while True: while True:
ready, tmp1, tmp2 = select.select([read_pipe], [], [], nextUpdate = min(tunnel_manager.next_refresh, peer_db.next_refresh)
max(0, min(tunnel_manager.next_refresh, if forward != None:
peer_db.next_refresh) - time.time())) nextUpdate = min(nextUpdate, forward.next_refresh)
nextUpdate = max(0, nextUpdate - time.time())
ready, tmp1, tmp2 = select.select([read_pipe], [], [], nextUpdate)
if ready: if ready:
peer_db.handle_message(read_pipe.readline()) peer_db.handle_message(read_pipe.readline())
if time.time() >= peer_db.next_refresh: if time.time() >= peer_db.next_refresh:
...@@ -147,4 +155,3 @@ def main(): ...@@ -147,4 +155,3 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main()
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