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 :
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
"Refreshing peers DB ... done", or add log messages to specify that an
action advertised by a previous log message has been completed
......@@ -8,20 +11,15 @@ To be done :
Use an algorithm to choose which connections to keep and/or establish
instead of pure randomness
|-> number of routes / tunnel : doesn't work
|-> number of routes / tunnel
|-> favorise most used roads ?
Replace comments at the beginning of functions with docstrings & give all
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
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 ....
( avahi could be used )
......@@ -51,14 +49,5 @@ To be discussed:
rapport
==> It takes babel between 3 times and 4 times the hello interval to
reestablish connection, if a direct link is cut
G : I think the number of route going through an interface should be a
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
U : So we have to reduce the hello interval. 2min to detect a dead link is
far too much.
import sqlite3, socket, xmlrpclib, time, os
import utils
class PeerManager:
# internal ip = temp arg/attribute
......@@ -16,7 +17,8 @@ class PeerManager:
self._proto = proto
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)
self._db = sqlite3.connect(os.path.join(db_dir_path, 'peers.db'),
......@@ -44,32 +46,43 @@ class PeerManager:
def _declare(self):
if self._address != None:
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:
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):
utils.log('Populating the peers DB', 2)
new_peer_list = self._proxy.getPeerList(self._db_size, self._internal_ip)
self._db.executemany("INSERT OR IGNORE INTO peers (prefix, address) VALUES (?,?)", new_peer_list)
new_peer_list = self._proxy.getPeerList(self._db_size,
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,))
utils.log('New peers : %s' % ', '.join(map(str, new_peer_list)), 5)
def getUnusedPeers(self, peer_count):
return self._db.execute("""SELECT prefix, address FROM peers WHERE used <= 0
ORDER BY used DESC,RANDOM() LIMIT ?""", (peer_count,))
return self._db.execute("""SELECT prefix, address FROM peers WHERE used
<= 0 ORDER BY used DESC,RANDOM() LIMIT ?""",
(peer_count,))
def usePeer(self, prefix):
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):
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):
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):
script_type, arg = msg.split()
......@@ -84,7 +97,9 @@ class PeerManager:
for proto in self._proto)
if 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()
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
log = None
smooth = 0.3
class Connection:
def __init__(self, address, write_pipe, hello, iface, prefix,
......@@ -11,7 +12,7 @@ class Connection:
self.process = plib.client(address, write_pipe, hello, '--dev', iface,
*ovpn_args, stdout=os.open(os.path.join(log,
'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.routes = 0
......@@ -24,7 +25,7 @@ class Connection:
def refresh(self):
# Check that the connection is alive
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)
return False
......@@ -42,22 +43,24 @@ class Connection:
t = time.time()
if bool(self._last_trafic):
bw = (trafic - self._last_trafic)/(t -
bw = (trafic - self._last_trafic) / (t -
self._last_trafic_update)
if bool(self._bandwidth):
self._bandwidth = (1-smooth)*self._bandwidth + smooth*bw
self._bandwidth = ((1 - smooth) * self._bandwidth
+ smooth * bw)
else:
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._last_trafic_update = t
self._last_trafic = trafic
except IOError: # This just means that the interface is downs
utils.log('Unable to calculate bandwidth on iface %s' %
except IOError: # This just means that the interface is downs
utils.log('Unable to calculate bandwidth on iface %s' %
self.iface, 4)
class TunnelManager:
def __init__(self, write_pipe, peer_db, openvpn_args, hello_interval,
......@@ -75,8 +78,8 @@ class TunnelManager:
'client10', 'client11', 'client12'))
self.next_refresh = time.time()
self._client_count = connection_count/2
self._refresh_count = refresh_rate*self._client_count
self._client_count = connection_count / 2
self._refresh_count = refresh_rate * self._client_count
def refresh(self):
utils.log('Refreshing the tunnels', 2)
......@@ -88,9 +91,9 @@ class TunnelManager:
def _cleanDeads(self):
for prefix in self._connection_dict.keys():
if not self._connection_dict[prefix].refresh():
self._kill(prefix)
self._peer_db.flagPeer(prefix)
def _removeSomeTunnels(self):
for i in range(0, max(0, len(self._connection_dict) -
......@@ -135,14 +138,12 @@ class TunnelManager:
self._connection_dict[self._iface_to_prefix[iface]].routes = 0
f = open('/proc/net/ipv6_route', 'r')
for line in f:
ip, subnet_size, iface = struct.unpack("""32s x 2s 106x
%ss x""" % (len(line)-142), line)
ip, subnet_size, iface = struct.unpack('32s x 2s 106x %ss x'
% (len(line) - 142), line)
iface = iface.replace(' ', '')
if iface in self._iface_to_prefix.keys():
self._connection_dict[self._iface_to_prefix[iface]].routes += 1
for p in self._connection_dict.keys():
utils.log('Routes on iface %s : %s' % (
self._connection_dict[p].iface,
self._connection_dict[p].routes ), 5)
self._connection_dict[p].routes), 5)
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:
while u.getspecificportmapping(external_port, 'UDP') != None :
external_port += 1
if external_port == 65536:
while self._u.getspecificportmapping(self.external_port,
'UDP') != None:
self.external_port += 1
if self.external_port == 65536:
raise Exception
if 'tcp-server' in protos:
while u.getspecificportmapping(external_port, 'TCP') != None :
external_port += 1
if external_port == 65536:
while self._u.getspecificportmapping(self.external_port,
'TCP') != None:
self.external_port += 1
if self._external_port == 65536:
raise Exception
if 'udp' in protos:
u.addportmapping(external_port, 'UDP', u.lanaddr, local_port,
'Vifib openvpn server', '')
self._u.addportmapping(self.external_port, 'UDP',
self._u.lanaddr, local_port, 'Vifib openvpn server', '')
if 'tcp-server' in protos:
u.addportmapping(external_port, 'TCP', u.lanaddr, local_port,
'Vifib openvpn server', '')
self._u.addportmapping(self.external_port, 'TCP',
self._u.lanaddr, local_port, 'Vifib openvpn server', '')
print (u.externalipaddress(), external_port)
return (u.externalipaddress(), external_port)
self.external_ip = self._u.externalipaddress()
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
from argparse import ArgumentParser
import db, plib, upnpigd, utils, tunnel
class ArgParser(ArgumentParser):
def convert_arg_line_to_args(self, arg_line):
......@@ -12,6 +13,7 @@ class ArgParser(ArgumentParser):
if arg.strip():
yield arg
def ovpnArgs(optional_args, ca_path, cert_path):
# Treat openvpn arguments
if optional_args[0] == "--":
......@@ -22,6 +24,7 @@ def ovpnArgs(optional_args, ca_path, cert_path):
optional_args.append(cert_path)
return optional_args
def getConfig():
parser = ArgParser(fromfile_prefix_chars='@',
description='Resilient virtual private network application')
......@@ -78,6 +81,7 @@ def getConfig():
help="Common OpenVPN options (e.g. certificates)")
return parser.parse_args()
def main():
# Get arguments
config = getConfig()
......@@ -101,10 +105,11 @@ def main():
else:
utils.log('Attempting automatic configuration via UPnP', 4)
try:
ext_ip, ext_port = upnpigd.ForwardViaUPnP(config.internal_port, config.proto)
config.address = list([ext_ip, str(ext_port), proto]
for proto in config.proto)
forward = upnpigd.UpnpForward(config.internal_port, config.proto)
config.address = list([forward.external_ip,
str(forward.external_port), proto] for proto in config.proto)
except Exception:
forward = None
utils.log('An atempt to forward a port via UPnP failed', 4)
peer_db = db.PeerManager(config.state, config.server, config.server_port,
......@@ -119,7 +124,7 @@ def main():
router = plib.router(network, internal_ip, interface_list, config.wireless,
config.hello, os.path.join(config.state, 'vifibnet.babeld.state'),
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
server_process = list(plib.server(internal_ip, len(network) + len(prefix),
......@@ -133,9 +138,12 @@ def main():
# main loop
try:
while True:
ready, tmp1, tmp2 = select.select([read_pipe], [], [],
max(0, min(tunnel_manager.next_refresh,
peer_db.next_refresh) - time.time()))
nextUpdate = min(tunnel_manager.next_refresh, peer_db.next_refresh)
if forward != None:
nextUpdate = min(nextUpdate, forward.next_refresh)
nextUpdate = max(0, nextUpdate - time.time())
ready, tmp1, tmp2 = select.select([read_pipe], [], [], nextUpdate)
if ready:
peer_db.handle_message(read_pipe.readline())
if time.time() >= peer_db.next_refresh:
......@@ -147,4 +155,3 @@ def main():
if __name__ == "__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