Bootstrap peer fixed in registry
[re6stnet.git] / tunnel.py
1 import os, random, traceback, time, struct, subprocess, operator, math
2 import plib, utils, db
3
4 log = None
5 smooth = 0.3 # this is used to smooth the traffic sampling. Lower value
6 # mean more smooth
7
8 # Be carfull the refresh interval should let the routes be established
9
10
11 class Connection:
12
13 def __init__(self, address, write_pipe, hello, iface, prefix,
14 ovpn_args):
15 self.process = plib.client(address, write_pipe, hello, '--dev', iface,
16 *ovpn_args, stdout=os.open(os.path.join(log,
17 'vifibnet.client.%s.log' % (prefix,)),
18 os.O_WRONLY | os.O_CREAT | os.O_TRUNC),
19 stderr=subprocess.STDOUT)
20
21 self.iface = iface
22 self.routes = 0
23 self._prefix = prefix
24 self._bandwidth = None
25 self._last_trafic = None
26
27 # TODO : update the stats
28 def refresh(self):
29 # Check that the connection is alive
30 if self.process.poll() != None:
31 utils.log('Connection with %s has failed with return code %s'
32 % (self._prefix, self.process.returncode), 3)
33 return False
34
35 self._updateBandwidth()
36 return True
37
38 def _updateBandwidth(self):
39 try:
40 f_rx = open('/sys/class/net/%s/statistics/rx_bytes' %
41 self.iface, 'r')
42 f_tx = open('/sys/class/net/%s/statistics/tx_bytes' %
43 self.iface, 'r')
44
45 trafic = int(f_rx.read()) + int(f_tx.read())
46 t = time.time()
47
48 if bool(self._last_trafic):
49 bw = (trafic - self._last_trafic) / (t -
50 self._last_trafic_update)
51 if bool(self._bandwidth):
52 self._bandwidth = ((1 - smooth) * self._bandwidth
53 + smooth * bw)
54 else:
55 self._bandwidth = bw
56
57 utils.log('New bandwidth calculated on iface %s : %s' %
58 (self.iface, self._bandwidth), 4)
59
60 self._last_trafic_update = t
61 self._last_trafic = trafic
62 except IOError: # This just means that the interface is downs
63 utils.log('Unable to calculate bandwidth on iface %s' %
64 self.iface, 4)
65
66
67 class TunnelManager:
68
69 def __init__(self, write_pipe, peer_db, openvpn_args, hello_interval,
70 refresh, connection_count, refresh_rate, iface_list, network):
71 self._write_pipe = write_pipe
72 self._peer_db = peer_db
73 self._connection_dict = {}
74 self._iface_to_prefix = {}
75 self._ovpn_args = openvpn_args
76 self._hello = hello_interval
77 self._refresh_time = refresh
78 self._network = network
79 self._net_len = len(network)
80 self._iface_list = iface_list
81 self.free_interface_set = set(('client1', 'client2', 'client3',
82 'client4', 'client5', 'client6',
83 'client7', 'client8', 'client9',
84 'client10', 'client11', 'client12'))
85 self.next_refresh = time.time()
86
87 self._client_count = int(math.ceil(float(connection_count) / 2.0))
88 self._refresh_count = int(math.ceil(refresh_rate * self._client_count))
89
90 def refresh(self):
91 utils.log('Refreshing the tunnels...', 2)
92 self._cleanDeads()
93 self._countRoutes()
94 self._removeSomeTunnels()
95 self._makeNewTunnels()
96 utils.log('Tunnels refreshed', 2)
97 self.next_refresh = time.time() + self._refresh_time
98
99 def _cleanDeads(self):
100 for prefix in self._connection_dict.keys():
101 if not self._connection_dict[prefix].refresh():
102 self._kill(prefix)
103 self._peer_db.flagPeer(prefix)
104
105 def _removeSomeTunnels(self):
106 # Get the candidates to killing
107 candidates = sorted(self._connection_dict, key=lambda p:
108 self._connection_dict[p].routes)
109 print max(0, len(self._connection_dict) - self._client_count + self._refresh_count) # DEBUG
110 print self._client_count
111 for prefix in candidates[0: max(0, len(self._connection_dict) -
112 self._client_count + self._refresh_count)]:
113 self._kill(prefix)
114
115 def _kill(self, prefix):
116 utils.log('Killing the connection with %s...' % (prefix,), 2)
117 connection = self._connection_dict.pop(prefix)
118 try:
119 connection.process.kill()
120 except OSError:
121 # If the process is already exited
122 pass
123 self.free_interface_set.add(connection.iface)
124 self._peer_db.unusePeer(prefix)
125 del self._iface_to_prefix[connection.iface]
126 utils.log('Connection with %s killed' % (prefix,), 2)
127
128 def _makeNewTunnels(self):
129 i = 0
130 utils.log('Trying to make %i new tunnels...' %
131 (self._client_count - len(self._connection_dict)), 5)
132 try:
133 for prefix, address in self._peer_db.getUnusedPeers(
134 self._client_count - len(self._connection_dict)):
135 utils.log('Establishing a connection with %s' % prefix, 2)
136 iface = self.free_interface_set.pop()
137 self._connection_dict[prefix] = Connection(address,
138 self._write_pipe, self._hello, iface,
139 prefix, self._ovpn_args)
140 self._iface_to_prefix[iface] = prefix
141 self._peer_db.usePeer(prefix)
142 i += 1
143 utils.log('%u new tunnels established' % (i,), 3)
144 except KeyError:
145 utils.log("""Can't establish connection with %s
146 : no available interface""" % prefix, 2)
147 except Exception:
148 traceback.print_exc()
149
150 def _countRoutes(self):
151 utils.log('Starting to count the routes on each interface...', 3)
152 self._peer_db.clear_blacklist(0)
153 for iface in self._iface_to_prefix.keys():
154 self._connection_dict[self._iface_to_prefix[iface]].routes = 0
155 for line in open('/proc/net/ipv6_route'):
156 line = line.split()
157 ip = bin(int(line[0], 16))[2:].rjust(128, '0')
158
159 if ip.startswith(self._network):
160 iface = line[-1]
161 subnet_size = int(line[1], 16)
162 utils.log('Route on iface %s detected to %s/%s'
163 % (iface, ip, subnet_size), 8)
164 if iface in self._iface_to_prefix.keys():
165 self._connection_dict[self._iface_to_prefix[iface]].routes += 1
166 if iface in self._iface_list and self._net_len < subnet_size < 128:
167 prefix = ip[self._net_len:subnet_size]
168 utils.log('A route to %s has been discovered on the LAN'
169 % (prefix,), 3)
170 self._peer_db.blacklist(prefix, 0)
171
172 utils.log("Routes have been counted", 3)
173 for p in self._connection_dict.keys():
174 utils.log('Routes on iface %s : %s' % (
175 self._connection_dict[p].iface,
176 self._connection_dict[p].routes), 5)
177