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