2 # -*- coding: utf-8 -*-
14 from nepi.util import server
16 class TunProtoBase(object):
17 def __init__(self, local, peer, home_path, key):
18 # Weak references, since ifaces do have a reference to the
19 # tunneling protocol implementation - we don't want strong
20 # circular references.
21 self.peer = weakref.ref(peer)
22 self.local = weakref.ref(local)
28 self.home_path = home_path
39 raise RuntimeError, "Lost reference to peering interfaces before launching"
41 raise RuntimeError, "Unconnected TUN - missing node"
43 # Make sure all the paths are created where
44 # they have to be created for deployment
45 cmd = "mkdir -p %s" % (server.shell_escape(self.home_path),)
46 (out,err),proc = server.popen_ssh_command(
48 host = local.node.hostname,
50 user = local.node.slicename,
52 ident_key = local.node.ident_path,
53 server_key = local.node.server_key
57 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
60 def _install_scripts(self):
64 raise RuntimeError, "Lost reference to peering interfaces before launching"
66 raise RuntimeError, "Unconnected TUN - missing node"
68 # Install the tun_connect script and tunalloc utility
69 from nepi.util import tunchannel
71 os.path.join(os.path.dirname(__file__), 'scripts', 'tun_connect.py'),
72 os.path.join(os.path.dirname(__file__), 'scripts', 'tunalloc.c'),
73 re.sub(r"([.]py)[co]$", r'\1', tunchannel.__file__, 1), # pyc/o files are version-specific
76 local.node.slicename, local.node.hostname,
77 os.path.join(self.home_path,'.'),)
78 (out,err),proc = server.popen_scp(
81 ident_key = local.node.ident_path,
82 server_key = local.node.server_key
86 raise RuntimeError, "Failed upload TUN connect script %r: %s %s" % (sources, out,err,)
88 # Make sure all dependencies are satisfied
89 local.node.wait_dependencies()
92 "cd %(home)s && gcc -fPIC -shared tunalloc.c -o tunalloc.so"
94 "wget -q -c -O python-passfd-src.tar.gz %(passfd_url)s && "
95 "mkdir -p python-passfd && "
96 "cd python-passfd && "
97 "tar xzf ../python-passfd-src.tar.gz --strip-components=1 && "
98 "python setup.py build && "
99 "python setup.py install --install-lib .. "
101 if local.tun_proto == "fd" else ""
104 'home' : server.shell_escape(self.home_path),
105 'passfd_url' : "http://yans.pl.sophia.inria.fr/code/hgwebdir.cgi/python-passfd/archive/2a6472c64c87.tar.gz",
107 (out,err),proc = server.popen_ssh_command(
109 host = local.node.hostname,
111 user = local.node.slicename,
113 ident_key = local.node.ident_path,
114 server_key = local.node.server_key
118 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
120 def launch(self, check_proto, listen, extra_args=[]):
124 if not peer or not local:
125 raise RuntimeError, "Lost reference to peering interfaces before launching"
127 peer_port = peer.tun_port
128 peer_addr = peer.tun_addr
129 peer_proto= peer.tun_proto
131 local_port = self.port
132 local_cap = local.capture
133 local_addr = local.address
134 local_mask = local.netprefix
135 local_snat = local.snat
136 local_txq = local.txqueuelen
137 local_p2p = local.pointopoint
139 if not local_p2p and hasattr(peer, 'address'):
140 local_p2p = peer.address
142 if check_proto != peer_proto:
143 raise RuntimeError, "Peering protocol mismatch: %s != %s" % (check_proto, peer_proto)
145 if not listen and ((peer_proto != 'fd' and not peer_port) or not peer_addr):
146 raise RuntimeError, "Misconfigured peer: %s" % (peer,)
148 if listen and ((peer_proto != 'fd' and not local_port) or not local_addr or not local_mask):
149 raise RuntimeError, "Misconfigured TUN: %s" % (local,)
151 args = ["python", "tun_connect.py",
152 "-m", str(self.mode),
153 "-A", str(local_addr),
154 "-M", str(local_mask)]
156 if check_proto == 'fd':
157 passfd_arg = str(peer_addr)
158 if passfd_arg.startswith('\x00'):
159 # cannot shell_encode null characters :(
160 passfd_arg = "base64:"+base64.b64encode(passfd_arg)
162 passfd_arg = '$HOME/'+server.shell_escape(passfd_arg)
164 "--pass-fd", passfd_arg
168 "-p", str(local_port if listen else peer_port),
175 args.extend(("-P",str(local_p2p)))
177 args.extend(("-Q",str(local_txq)))
179 args.extend(map(str,extra_args))
180 if not listen and check_proto != 'fd':
181 args.append(str(peer_addr))
184 self._install_scripts()
186 # Start process in a "daemonized" way, using nohup and heavy
187 # stdin/out redirection to avoid connection issues
188 (out,err),proc = rspawn.remote_spawn(
192 home = self.home_path,
194 stdout = 'capture' if local_cap else '/dev/null',
195 stderr = rspawn.STDOUT,
198 host = local.node.hostname,
200 user = local.node.slicename,
202 ident_key = local.node.ident_path,
203 server_key = local.node.server_key
207 raise RuntimeError, "Failed to set up TUN: %s %s" % (out,err,)
211 def _launch_and_wait(self, *p, **kw):
214 self.launch(*p, **kw)
216 # Wait for the process to be started
217 while self.status() == rspawn.NOT_STARTED:
220 # Wait for the connection to be established
222 for spin in xrange(30):
223 if self.status() != rspawn.RUNNING:
226 (out,err),proc = server.popen_ssh_command(
227 "cd %(home)s ; grep -c Connected capture" % dict(
228 home = server.shell_escape(self.home_path)),
229 host = local.node.hostname,
231 user = local.node.slicename,
233 ident_key = local.node.ident_path,
234 server_key = local.node.server_key
240 if out.strip() != '0':
245 def async_launch(self, check_proto, listen, extra_args=[]):
246 if not self._launcher:
247 self._launcher = threading.Thread(
248 target = self._launch_and_wait,
249 args = (check_proto, listen, extra_args))
250 self._launcher.start()
252 def async_launch_wait(self):
254 self._launcher.join()
255 if not self._started:
256 raise RuntimeError, "Failed to launch TUN forwarder"
257 elif not self._started:
264 raise RuntimeError, "Lost reference to local interface"
267 # NOTE: wait a bit for the pidfile to be created
268 if self._started and not self._pid or not self._ppid:
269 pidtuple = rspawn.remote_check_pid(
270 os.path.join(self.home_path,'pid'),
271 host = local.node.hostname,
273 user = local.node.slicename,
275 ident_key = local.node.ident_path,
276 server_key = local.node.server_key
280 self._pid, self._ppid = pidtuple
286 raise RuntimeError, "Lost reference to local interface"
289 if not self._started:
290 return rspawn.NOT_STARTED
291 elif not self._pid or not self._ppid:
292 return rspawn.NOT_STARTED
294 status = rspawn.remote_status(
295 self._pid, self._ppid,
296 host = local.node.hostname,
298 user = local.node.slicename,
300 ident_key = local.node.ident_path
308 raise RuntimeError, "Lost reference to local interface"
310 status = self.status()
311 if status == rspawn.RUNNING:
312 # kill by ppid+pid - SIGTERM first, then try SIGKILL
314 self._pid, self._ppid,
315 host = local.node.hostname,
317 user = local.node.slicename,
319 ident_key = local.node.ident_path,
320 server_key = local.node.server_key,
324 def sync_trace(self, local_dir, whichtrace):
325 if whichtrace != 'packets':
333 local_path = os.path.join(local_dir, 'capture')
335 # create parent local folders
336 proc = subprocess.Popen(
337 ["mkdir", "-p", os.path.dirname(local_path)],
338 stdout = open("/dev/null","w"),
339 stdin = open("/dev/null","r"))
342 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
345 (out,err),proc = server.popen_scp(
346 '%s@%s:%s' % (local.node.slicename, local.node.hostname,
347 os.path.join(self.home_path, 'capture')),
351 ident_key = local.node.ident_path,
352 server_key = local.node.server_key
356 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
365 eg: set up listening ports
367 raise NotImplementedError
375 raise NotImplementedError
381 raise NotImplementedError
384 class TunProtoUDP(TunProtoBase):
385 def __init__(self, local, peer, home_path, key, listening):
386 super(TunProtoUDP, self).__init__(local, peer, home_path, key)
387 self.listening = listening
393 self.async_launch('udp', False, ("-u",str(self.port)))
398 class TunProtoFD(TunProtoBase):
399 def __init__(self, local, peer, home_path, key, listening):
400 super(TunProtoFD, self).__init__(local, peer, home_path, key)
401 self.listening = listening
407 self.async_launch('fd', False)
412 class TunProtoTCP(TunProtoBase):
413 def __init__(self, local, peer, home_path, key, listening):
414 super(TunProtoTCP, self).__init__(local, peer, home_path, key)
415 self.listening = listening
419 self.async_launch('tcp', True)
422 if not self.listening:
423 # make sure our peer is ready
425 if peer and peer.peer_proto_impl:
426 peer.peer_proto_impl.async_launch_wait()
428 if not self._started:
429 self.launch('tcp', False)
431 # make sure WE are ready
432 self.async_launch_wait()
439 class TapProtoUDP(TunProtoUDP):
440 def __init__(self, local, peer, home_path, key, listening):
441 super(TapProtoUDP, self).__init__(local, peer, home_path, key, listening)
444 class TapProtoTCP(TunProtoTCP):
445 def __init__(self, local, peer, home_path, key, listening):
446 super(TapProtoTCP, self).__init__(local, peer, home_path, key, listening)
449 class TapProtoFD(TunProtoFD):
450 def __init__(self, local, peer, home_path, key, listening):
451 super(TapProtoFD, self).__init__(local, peer, home_path, key, listening)