Remove some debug print
[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('Checking the tunnels...')
58 self._cleanDeads()
59 if self._next_tunnel_refresh < time.time():
60 logging.info('Refreshing the tunnels...')
61 self._countRoutes()
62 self._removeSomeTunnels()
63 self._next_tunnel_refresh = time.time() + self._refresh_time
64 self._makeNewTunnels()
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
96 def _makeNewTunnels(self):
97 tunnel_to_make = self._client_count - len(self._connection_dict)
98 if tunnel_to_make <= 0:
99 return
100
101 i = 0
102 logging.trace('Trying to make %i new tunnels...' % tunnel_to_make)
103 try:
104 for prefix, address in self._peer_db.getUnusedPeers(tunnel_to_make):
105 logging.info('Establishing a connection with %s/%u' %
106 (hex(int(prefix, 2))[2:], len(prefix)))
107 iface = self.free_interface_set.pop()
108 self._connection_dict[prefix] = Connection(address,
109 self._write_pipe, self._hello, iface,
110 prefix, self._ovpn_args)
111 self._iface_to_prefix[iface] = prefix
112 self._peer_db.usePeer(prefix)
113 i += 1
114 logging.trace('%u new tunnels established' % (i,))
115 except KeyError:
116 logging.warning("""Can't establish connection with %s
117 : no available interface""" % prefix)
118 except Exception:
119 traceback.print_exc()
120
121 def _countRoutes(self):
122 logging.debug('Starting to count the routes on each interface...')
123 self._peer_db.clear_blacklist(0)
124 for iface in self._iface_to_prefix.keys():
125 self._connection_dict[self._iface_to_prefix[iface]].routes = 0
126 for line in open('/proc/net/ipv6_route'):
127 line = line.split()
128 ip = bin(int(line[0], 16))[2:].rjust(128, '0')
129
130 if ip.startswith(self._network):
131 iface = line[-1]
132 subnet_size = int(line[1], 16)
133 logging.trace('Route on iface %s detected to %s/%s'
134 % (iface, ip, subnet_size))
135 if iface in self._iface_to_prefix.keys():
136 self._connection_dict[self._iface_to_prefix[iface]].routes += 1
137 if iface in self._iface_list and self._net_len < subnet_size < 128:
138 prefix = ip[self._net_len:subnet_size]
139 logging.debug('A route to %s has been discovered on the LAN'
140 % (hex(int(prefix), 2)[2:]))
141 self._peer_db.blacklist(prefix, 0)
142
143 logging.debug("Routes have been counted")
144 for p in self._connection_dict.keys():
145 logging.trace('Routes on iface %s : %s' % (
146 self._connection_dict[p].iface,
147 self._connection_dict[p].routes))
148
149 def killAll(self):
150 for prefix in self._connection_dict.keys():
151 self._kill(prefix)
152
153 def checkIncommingTunnel(self, prefix):
154 if prefix in self._connection_dict:
155 if prefix >= self._prefix:
156 self._kill(prefix)
157 return True
158 else:
159 return False
160 else:
161 return True