import os, random, traceback, time, struct, subprocess import plib, utils, db log = None smooth = 0.3 class Connection: def __init__(self, address, write_pipe, hello, iface, prefix, ovpn_args): 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), stderr=subprocess.STDOUT) self.iface = iface self.routes = 0 self._prefix = prefix self._creation_date = time.time() self._bandwidth = None self._last_trafic = None # TODO : update the stats 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' % (self._prefix, self.process.returncode), 3) return False self._updateBandwidth() return True def _updateBandwidth(self): try: f_rx = open('/sys/class/net/%s/statistics/rx_bytes' % self.iface, 'r') f_tx = open('/sys/class/net/%s/statistics/tx_bytes' % self.iface, 'r') trafic = int(f_rx.read()) + int(f_tx.read()) t = time.time() if bool(self._last_trafic): bw = (trafic - self._last_trafic) / (t - self._last_trafic_update) if bool(self._bandwidth): self._bandwidth = ((1 - smooth) * self._bandwidth + smooth * bw) else: self._bandwidth = bw 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' % self.iface, 4) class TunnelManager: def __init__(self, write_pipe, peer_db, openvpn_args, hello_interval, refresh, connection_count, refresh_rate, iface_list, network): self._write_pipe = write_pipe self._peer_db = peer_db self._connection_dict = {} self._iface_to_prefix = {} self._ovpn_args = openvpn_args self._hello = hello_interval self._refresh_time = refresh self._network = network self._net_len = len(network) self._iface_list = iface_list self.__indirect_connect = [] self.free_interface_set = set(('client1', 'client2', 'client3', 'client4', 'client5', 'client6', 'client7', 'client8', 'client9', 'client10', 'client11', 'client12')) self.next_refresh = time.time() self._client_count = connection_count / 2 self._refresh_count = refresh_rate * self._client_count def refresh(self): utils.log('Refreshing the tunnels...', 2) self._cleanDeads() self._countRoutes() self._removeSomeTunnels() self._makeNewTunnels() utils.log('Tunnels refreshed', 2) self.next_refresh = time.time() + self._refresh_time 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) - self._client_count + self._refresh_count)): prefix = random.choice(self._connection_dict.keys()) self._kill(prefix) def _kill(self, prefix): utils.log('Killing the connection with %s...' % (prefix,), 2) connection = self._connection_dict.pop(prefix) try: connection.process.kill() except OSError: # If the process is already exited pass self.free_interface_set.add(connection.iface) self._peer_db.unusePeer(prefix) del self._iface_to_prefix[connection.iface] utils.log('Connection with %s killed' % (prefix,), 2) def _makeNewTunnels(self): i = 0 utils.log('Trying to make %i new tunnels...' % (self._client_count - len(self._connection_dict)), 5) try: for prefix, address in self._peer_db.getUnusedPeers( self._client_count - len(self._connection_dict)): utils.log('Establishing a connection with %s' % prefix, 2) iface = self.free_interface_set.pop() self._connection_dict[prefix] = Connection(address, self._write_pipe, self._hello, iface, prefix, self._ovpn_args) self._iface_to_prefix[iface] = prefix self._peer_db.usePeer(prefix) i += 1 utils.log('%u new tunnels established' %(i,), 3) except KeyError: utils.log("""Can't establish connection with %s : no available interface""" % prefix, 2) except Exception: traceback.print_exc() def _countRoutes(self): utils.log('Starting to count the routes on each interface...', 3) self._indirect_connect = [] for iface in self._iface_to_prefix.keys(): 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) iface = iface.replace(' ', '') utils.log('Route on iface %s detected to %s/%s' % (iface,ip, subnet_size), 8) if iface in self._iface_to_prefix.keys(): self._connection_dict[self._iface_to_prefix[iface]].routes += 1 if iface in self._iface_list: subnet_size = int(subnet_size,16) ip = bin(int(ip, 16))[2:].rjust(128, '0') if self._net_len < subnet_size < 128 and ip.startswith(self._network): prefix = ip[self._net_len:subnet_size] utils.log('A route to %s has been discovered on the LAN' % (prefix,), 3) self._peer_db.blacklist(prefix) utils.log("Routes have been counted", 3) 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)