TODO update
[re6stnet.git] / tunnel.py
1 import os, random, traceback, time, struct
2 import plib, utils, db
3
4 log = None
5 smooth = 0.3
6
7
8 class Connection:
9
10 def __init__(self, address, write_pipe, hello, iface, prefix,
11 ovpn_args):
12 self.process = plib.client(address, write_pipe, hello, '--dev', iface,
13 *ovpn_args, stdout=os.open(os.path.join(log,
14 'vifibnet.client.%s.log' % (prefix,)),
15 os.O_WRONLY | os.O_CREAT | os.O_TRUNC))
16
17 self.iface = iface
18 self.routes = 0
19 self._prefix = prefix
20 self._creation_date = time.time()
21 self._bandwidth = None
22 self._last_trafic = None
23
24 # TODO : update the stats
25 def refresh(self):
26 # Check that the connection is alive
27 if self.process.poll() != None:
28 utils.log('Connection with %s has failed with return code %s'
29 % (self._prefix, self.process.returncode), 3)
30 return False
31
32 self._updateBandwidth()
33 return True
34
35 def _updateBandwidth(self):
36 try:
37 f_rx = open('/sys/class/net/%s/statistics/rx_bytes' %
38 self.iface, 'r')
39 f_tx = open('/sys/class/net/%s/statistics/tx_bytes' %
40 self.iface, 'r')
41
42 trafic = int(f_rx.read()) + int(f_tx.read())
43 t = time.time()
44
45 if bool(self._last_trafic):
46 bw = (trafic - self._last_trafic) / (t -
47 self._last_trafic_update)
48 if bool(self._bandwidth):
49 self._bandwidth = ((1 - smooth) * self._bandwidth
50 + smooth * bw)
51 else:
52 self._bandwidth = bw
53
54 utils.log('New bandwidth calculated on iface %s : %s' %
55 (self.iface, self._bandwidth), 4)
56
57 self._last_trafic_update = t
58 self._last_trafic = trafic
59 except IOError: # This just means that the interface is downs
60 utils.log('Unable to calculate bandwidth on iface %s' %
61 self.iface, 4)
62
63
64 class TunnelManager:
65
66 def __init__(self, write_pipe, peer_db, openvpn_args, hello_interval,
67 refresh, connection_count, refresh_rate):
68 self._write_pipe = write_pipe
69 self._peer_db = peer_db
70 self._connection_dict = {}
71 self._iface_to_prefix = {}
72 self._ovpn_args = openvpn_args
73 self._hello = hello_interval
74 self._refresh_time = refresh
75 self.free_interface_set = set(('client1', 'client2', 'client3',
76 'client4', 'client5', 'client6',
77 'client7', 'client8', 'client9',
78 'client10', 'client11', 'client12'))
79 self.next_refresh = time.time()
80
81 self._client_count = connection_count / 2
82 self._refresh_count = refresh_rate * self._client_count
83
84 def refresh(self):
85 utils.log('Refreshing the tunnels', 2)
86 self._cleanDeads()
87 self._countRoutes()
88 self._removeSomeTunnels()
89 self._makeNewTunnels()
90 self.next_refresh = time.time() + self._refresh_time
91
92 def _cleanDeads(self):
93 for prefix in self._connection_dict.keys():
94 if not self._connection_dict[prefix].refresh():
95 self._kill(prefix)
96 self._peer_db.flagPeer(prefix)
97
98 def _removeSomeTunnels(self):
99 for i in range(0, max(0, len(self._connection_dict) -
100 self._client_count + self._refresh_count)):
101 prefix = random.choice(self._connection_dict.keys())
102 self._kill(prefix)
103
104 def _kill(self, prefix):
105 utils.log('Killing the connection with ' + prefix, 2)
106 connection = self._connection_dict.pop(prefix)
107 try:
108 connection.process.kill()
109 except OSError:
110 # If the process is already exited
111 pass
112 self.free_interface_set.add(connection.iface)
113 self._peer_db.unusePeer(prefix)
114 del self._iface_to_prefix[connection.iface]
115
116 def _makeNewTunnels(self):
117 utils.log('Trying to make %i new tunnels' %
118 (self._client_count - len(self._connection_dict)), 5)
119 try:
120 for prefix, address in self._peer_db.getUnusedPeers(
121 self._client_count - len(self._connection_dict)):
122 utils.log('Establishing a connection with %s' % prefix, 2)
123 iface = self.free_interface_set.pop()
124 self._connection_dict[prefix] = Connection(address,
125 self._write_pipe, self._hello, iface,
126 prefix, self._ovpn_args)
127 self._iface_to_prefix[iface] = prefix
128 self._peer_db.usePeer(prefix)
129 except KeyError:
130 utils.log("""Can't establish connection with %s
131 : no available interface""" % prefix, 2)
132 except Exception:
133 traceback.print_exc()
134
135 def _countRoutes(self):
136 utils.log('Starting to count the routes on each interface', 3)
137 for iface in self._iface_to_prefix.keys():
138 self._connection_dict[self._iface_to_prefix[iface]].routes = 0
139 f = open('/proc/net/ipv6_route', 'r')
140 for line in f:
141 ip, subnet_size, iface = struct.unpack('32s x 2s 106x %ss x'
142 % (len(line) - 142), line)
143 iface = iface.replace(' ', '')
144 if iface in self._iface_to_prefix.keys():
145 self._connection_dict[self._iface_to_prefix[iface]].routes += 1
146 for p in self._connection_dict.keys():
147 utils.log('Routes on iface %s : %s' % (
148 self._connection_dict[p].iface,
149 self._connection_dict[p].routes), 5)