2 # -*- coding: utf-8 -*-
11 from nepi.util import server
13 class TunProtoBase(object):
14 def __init__(self, local, peer, home_path, key):
15 # Weak references, since ifaces do have a reference to the
16 # tunneling protocol implementation - we don't want strong
17 # circular references.
18 self.peer = weakref.ref(peer)
19 self.local = weakref.ref(local)
25 self.home_path = home_path
36 raise RuntimeError, "Lost reference to peering interfaces before launching"
38 raise RuntimeError, "Unconnected TUN - missing node"
40 # Make sure all the paths are created where
41 # they have to be created for deployment
42 cmd = "mkdir -p %s" % (server.shell_escape(self.home_path),)
43 (out,err),proc = server.popen_ssh_command(
45 host = local.node.hostname,
47 user = local.node.slicename,
49 ident_key = local.node.ident_path,
50 server_key = local.node.server_key
54 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
57 def _install_scripts(self):
61 raise RuntimeError, "Lost reference to peering interfaces before launching"
63 raise RuntimeError, "Unconnected TUN - missing node"
65 # Install the tun_connect script and tunalloc utility
67 os.path.join(os.path.dirname(__file__), 'scripts', 'tun_connect.py'),
68 os.path.join(os.path.dirname(__file__), 'scripts', 'tunalloc.c'),
71 local.node.slicename, local.node.hostname,
72 os.path.join(self.home_path,'.'),)
73 (out,err),proc = server.popen_scp(
76 ident_key = local.node.ident_path,
77 server_key = local.node.server_key
81 raise RuntimeError, "Failed upload TUN connect script %r: %s %s" % (source, out,err,)
84 "cd %(home)s && gcc -shared tunalloc.c -o tunalloc.so"
86 "wget -q -c -O python-passfd-src.tar.gz %(passfd_url)s && "
87 "mkdir -p python-passfd && "
88 "cd python-passfd && "
89 "tar xzf ../python-passfd-src.tar.gz --strip-components=1 && "
90 "python setup.py build && "
91 "python setup.py install --install-lib .. "
93 if local.tun_proto == "fd" else ""
96 'home' : server.shell_escape(self.home_path),
97 'passfd_url' : "http://yans.pl.sophia.inria.fr/code/hgwebdir.cgi/python-passfd/archive/2a6472c64c87.tar.gz",
99 (out,err),proc = server.popen_ssh_command(
101 host = local.node.hostname,
103 user = local.node.slicename,
105 ident_key = local.node.ident_path,
106 server_key = local.node.server_key
110 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
112 def launch(self, check_proto, listen, extra_args=[]):
116 if not peer or not local:
117 raise RuntimeError, "Lost reference to peering interfaces before launching"
119 peer_port = peer.tun_port
120 peer_addr = peer.tun_addr
121 peer_proto= peer.tun_proto
123 local_port = self.port
124 local_cap = local.capture
125 local_addr = local.address
126 local_mask = local.netprefix
127 local_snat = local.snat
128 local_txq = local.txqueuelen
130 if check_proto != peer_proto:
131 raise RuntimeError, "Peering protocol mismatch: %s != %s" % (check_proto, peer_proto)
133 if not listen and (not peer_port or not peer_addr):
134 raise RuntimeError, "Misconfigured peer: %s" % (peer,)
136 if listen and (not local_port or not local_addr or not local_mask):
137 raise RuntimeError, "Misconfigured TUN: %s" % (local,)
139 args = ["python", "tun_connect.py",
140 "-m", str(self.mode)]
142 if check_proto == 'fd':
144 "--pass-fd", str(peer_addr)])
147 "-p", str(local_port if listen else peer_port),
148 "-A", str(local_addr),
149 "-M", str(local_mask),
150 "-k", str(self.key)])
155 args.extend(("-Q",str(local_txq)))
157 args.extend(map(str,extra_args))
159 args.append(str(peer_addr))
162 self._install_scripts()
164 # Start process in a "daemonized" way, using nohup and heavy
165 # stdin/out redirection to avoid connection issues
166 (out,err),proc = rspawn.remote_spawn(
170 home = self.home_path,
172 stdout = 'capture' if local_cap else '/dev/null',
173 stderr = rspawn.STDOUT,
176 host = local.node.hostname,
178 user = local.node.slicename,
180 ident_key = local.node.ident_path,
181 server_key = local.node.server_key
185 raise RuntimeError, "Failed to set up TUN: %s %s" % (out,err,)
189 def async_launch(self, check_proto, listen, extra_args=[]):
190 if not self._launcher:
191 self._launcher = threading.Thread(
192 target = self.launch,
193 args = (check_proto, listen, extra_args))
194 self._launcher.start()
196 def async_launch_wait(self):
197 if not self._started:
199 self._launcher.join()
200 if not self._started:
201 raise RuntimeError, "Failed to launch TUN forwarder"
209 raise RuntimeError, "Lost reference to local interface"
212 # NOTE: wait a bit for the pidfile to be created
213 if self._started and not self._pid or not self._ppid:
214 pidtuple = rspawn.remote_check_pid(
215 os.path.join(self.home_path,'pid'),
216 host = local.node.hostname,
218 user = local.node.slicename,
220 ident_key = local.node.ident_path,
221 server_key = local.node.server_key
225 self._pid, self._ppid = pidtuple
231 raise RuntimeError, "Lost reference to local interface"
234 if not self._started:
235 return rspawn.NOT_STARTED
236 elif not self._pid or not self._ppid:
237 return rspawn.NOT_STARTED
239 status = rspawn.remote_status(
240 self._pid, self._ppid,
241 host = local.node.hostname,
243 user = local.node.slicename,
245 ident_key = local.node.ident_path
253 raise RuntimeError, "Lost reference to local interface"
255 status = self.status()
256 if status == rspawn.RUNNING:
257 # kill by ppid+pid - SIGTERM first, then try SIGKILL
259 self._pid, self._ppid,
260 host = local.node.hostname,
262 user = local.node.slicename,
264 ident_key = local.node.ident_path,
265 server_key = local.node.server_key,
269 def sync_trace(self, local_dir, whichtrace):
270 if whichtrace != 'packets':
278 local_path = os.path.join(local_dir, 'capture')
280 # create parent local folders
281 proc = subprocess.Popen(
282 ["mkdir", "-p", os.path.dirname(local_path)],
283 stdout = open("/dev/null","w"),
284 stdin = open("/dev/null","r"))
287 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
290 (out,err),proc = server.popen_scp(
291 '%s@%s:%s' % (local.node.slicename, local.node.hostname,
292 os.path.join(self.home_path, 'capture')),
296 ident_key = local.node.ident_path,
297 server_key = local.node.server_key
301 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
310 eg: set up listening ports
312 raise NotImplementedError
320 raise NotImplementedError
326 raise NotImplementedError
329 class TunProtoUDP(TunProtoBase):
330 def __init__(self, local, peer, home_path, key, listening):
331 super(TunProtoUDP, self).__init__(local, peer, home_path, key)
332 self.listening = listening
338 self.async_launch('udp', False, ("-u",str(self.port)))
343 class TunProtoFD(TunProtoBase):
344 def __init__(self, local, peer, home_path, key, listening):
345 super(TunProtoFD, self).__init__(local, peer, home_path, key)
346 self.listening = listening
352 self.async_launch('fd', False)
357 class TunProtoTCP(TunProtoBase):
358 def __init__(self, local, peer, home_path, key, listening):
359 super(TunProtoTCP, self).__init__(local, peer, home_path, key)
360 self.listening = listening
364 self.async_launch('tcp', True)
367 if not self.listening:
368 # make sure our peer is ready
370 if peer and peer.peer_proto_impl:
371 peer.peer_proto_impl.async_launch_wait()
373 self.launch('tcp', False)
375 # make sure WE are ready
376 self.async_launch_wait()
383 class TapProtoUDP(TunProtoUDP):
384 def __init__(self, local, peer, home_path, key, listening):
385 super(TapProtoUDP, self).__init__(local, peer, home_path, key, listening)
388 class TapProtoTCP(TunProtoTCP):
389 def __init__(self, local, peer, home_path, key, listening):
390 super(TapProtoTCP, self).__init__(local, peer, home_path, key, listening)
393 class TapProtoFD(TunProtoFD):
394 def __init__(self, local, peer, home_path, key, listening):
395 super(TapProtoUDP, self).__init__(local, peer, home_path, key, listening)