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