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
32 self._starting = False
41 raise RuntimeError, "Lost reference to peering interfaces before launching"
43 raise RuntimeError, "Unconnected TUN - missing node"
45 # Make sure all the paths are created where
46 # they have to be created for deployment
47 # Also remove pidfile, if there is one.
48 # Old pidfiles from previous runs can be troublesome.
49 cmd = "mkdir -p %(home)s ; rm -f %(home)s/pid" % {
50 'home' : server.shell_escape(self.home_path)
52 (out,err),proc = server.popen_ssh_command(
54 host = local.node.hostname,
56 user = local.node.slicename,
58 ident_key = local.node.ident_path,
59 server_key = local.node.server_key
63 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
66 def _install_scripts(self):
70 raise RuntimeError, "Lost reference to peering interfaces before launching"
72 raise RuntimeError, "Unconnected TUN - missing node"
74 # Install the tun_connect script and tunalloc utility
75 from nepi.util import tunchannel
77 os.path.join(os.path.dirname(__file__), 'scripts', 'tun_connect.py'),
78 os.path.join(os.path.dirname(__file__), 'scripts', 'tunalloc.c'),
79 re.sub(r"([.]py)[co]$", r'\1', tunchannel.__file__, 1), # pyc/o files are version-specific
82 local.node.slicename, local.node.hostname,
83 os.path.join(self.home_path,'.'),)
84 (out,err),proc = server.popen_scp(
87 ident_key = local.node.ident_path,
88 server_key = local.node.server_key
92 raise RuntimeError, "Failed upload TUN connect script %r: %s %s" % (sources, out,err,)
94 # Make sure all dependencies are satisfied
95 local.node.wait_dependencies()
98 "cd %(home)s && gcc -fPIC -shared tunalloc.c -o tunalloc.so"
100 "wget -q -c -O python-passfd-src.tar.gz %(passfd_url)s && "
101 "mkdir -p python-passfd && "
102 "cd python-passfd && "
103 "tar xzf ../python-passfd-src.tar.gz --strip-components=1 && "
104 "python setup.py build && "
105 "python setup.py install --install-lib .. "
107 if local.tun_proto == "fd" else ""
110 'home' : server.shell_escape(self.home_path),
111 'passfd_url' : "http://yans.pl.sophia.inria.fr/code/hgwebdir.cgi/python-passfd/archive/2a6472c64c87.tar.gz",
113 (out,err),proc = server.popen_ssh_command(
115 host = local.node.hostname,
117 user = local.node.slicename,
119 ident_key = local.node.ident_path,
120 server_key = local.node.server_key
124 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
126 def launch(self, check_proto, listen, extra_args=[]):
128 raise AssertionError, "Double start"
130 self._starting = True
135 if not peer or not local:
136 raise RuntimeError, "Lost reference to peering interfaces before launching"
138 peer_port = peer.tun_port
139 peer_addr = peer.tun_addr
140 peer_proto= peer.tun_proto
142 local_port = self.port
143 local_cap = local.capture
144 local_addr = local.address
145 local_mask = local.netprefix
146 local_snat = local.snat
147 local_txq = local.txqueuelen
148 local_p2p = local.pointopoint
150 if not local_p2p and hasattr(peer, 'address'):
151 local_p2p = peer.address
153 if check_proto != peer_proto:
154 raise RuntimeError, "Peering protocol mismatch: %s != %s" % (check_proto, peer_proto)
156 if not listen and ((peer_proto != 'fd' and not peer_port) or not peer_addr):
157 raise RuntimeError, "Misconfigured peer: %s" % (peer,)
159 if listen and ((peer_proto != 'fd' and not local_port) or not local_addr or not local_mask):
160 raise RuntimeError, "Misconfigured TUN: %s" % (local,)
162 args = ["python", "tun_connect.py",
163 "-m", str(self.mode),
164 "-A", str(local_addr),
165 "-M", str(local_mask)]
167 if check_proto == 'fd':
168 passfd_arg = str(peer_addr)
169 if passfd_arg.startswith('\x00'):
170 # cannot shell_encode null characters :(
171 passfd_arg = "base64:"+base64.b64encode(passfd_arg)
173 passfd_arg = '$HOME/'+server.shell_escape(passfd_arg)
175 "--pass-fd", passfd_arg
179 "-p", str(local_port if listen else peer_port),
186 args.extend(("-P",str(local_p2p)))
188 args.extend(("-Q",str(local_txq)))
192 args.extend(map(str,extra_args))
193 if not listen and check_proto != 'fd':
194 args.append(str(peer_addr))
197 self._install_scripts()
199 # Start process in a "daemonized" way, using nohup and heavy
200 # stdin/out redirection to avoid connection issues
201 (out,err),proc = rspawn.remote_spawn(
205 home = self.home_path,
208 stderr = rspawn.STDOUT,
211 host = local.node.hostname,
213 user = local.node.slicename,
215 ident_key = local.node.ident_path,
216 server_key = local.node.server_key
220 raise RuntimeError, "Failed to set up TUN: %s %s" % (out,err,)
224 def _launch_and_wait(self, *p, **kw):
226 self.__launch_and_wait(*p, **kw)
230 self._launcher._exc.append(sys.exc_info())
234 def __launch_and_wait(self, *p, **kw):
237 self.launch(*p, **kw)
239 # Wait for the process to be started
240 while self.status() == rspawn.NOT_STARTED:
243 # Wait for the connection to be established
244 for spin in xrange(30):
245 if self.status() != rspawn.RUNNING:
248 (out,err),proc = server.popen_ssh_command(
249 "cd %(home)s ; grep -c Connected capture" % dict(
250 home = server.shell_escape(self.home_path)),
251 host = local.node.hostname,
253 user = local.node.slicename,
255 ident_key = local.node.ident_path,
256 server_key = local.node.server_key
262 if out.strip() != '0':
269 if not self._if_name:
270 # Inspect the trace to check the assigned iface
273 for spin in xrange(30):
274 (out,err),proc = server.popen_ssh_command(
275 "cd %(home)s ; grep 'Using tun:' capture | head -1" % dict(
276 home = server.shell_escape(self.home_path)),
277 host = local.node.hostname,
279 user = local.node.slicename,
281 ident_key = local.node.ident_path,
282 server_key = local.node.server_key
290 match = re.match(r"Using +tun: +([-a-zA-Z0-9]*) +.*",out)
292 self._if_name = match.group(1)
295 def async_launch(self, check_proto, listen, extra_args=[]):
296 if not self._launcher:
297 self._launcher = threading.Thread(
298 target = self._launch_and_wait,
299 args = (check_proto, listen, extra_args))
300 self._launcher._exc = []
301 self._launcher.start()
303 def async_launch_wait(self):
305 self._launcher.join()
306 if not self._started:
307 if self._launcher._exc:
308 exctyp,exval,exctrace = self._launcher._exc[0]
309 raise exctyp,exval,exctrace
311 raise RuntimeError, "Failed to launch TUN forwarder"
312 elif not self._started:
319 raise RuntimeError, "Lost reference to local interface"
322 # NOTE: wait a bit for the pidfile to be created
323 if self._started and not self._pid or not self._ppid:
324 pidtuple = rspawn.remote_check_pid(
325 os.path.join(self.home_path,'pid'),
326 host = local.node.hostname,
328 user = local.node.slicename,
330 ident_key = local.node.ident_path,
331 server_key = local.node.server_key
335 self._pid, self._ppid = pidtuple
341 raise RuntimeError, "Lost reference to local interface"
344 if not self._started:
345 return rspawn.NOT_STARTED
346 elif not self._pid or not self._ppid:
347 return rspawn.NOT_STARTED
349 status = rspawn.remote_status(
350 self._pid, self._ppid,
351 host = local.node.hostname,
353 user = local.node.slicename,
355 ident_key = local.node.ident_path
363 raise RuntimeError, "Lost reference to local interface"
365 status = self.status()
366 if status == rspawn.RUNNING:
367 # kill by ppid+pid - SIGTERM first, then try SIGKILL
369 self._pid, self._ppid,
370 host = local.node.hostname,
372 user = local.node.slicename,
374 ident_key = local.node.ident_path,
375 server_key = local.node.server_key,
383 status = self.status()
384 if status != rspawn.RUNNING:
387 interval = min(30.0, interval * 1.1)
389 def sync_trace(self, local_dir, whichtrace):
390 if whichtrace != 'packets':
398 local_path = os.path.join(local_dir, 'capture')
400 # create parent local folders
401 proc = subprocess.Popen(
402 ["mkdir", "-p", os.path.dirname(local_path)],
403 stdout = open("/dev/null","w"),
404 stdin = open("/dev/null","r"))
407 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
410 (out,err),proc = server.popen_scp(
411 '%s@%s:%s' % (local.node.slicename, local.node.hostname,
412 os.path.join(self.home_path, 'capture')),
416 ident_key = local.node.ident_path,
417 server_key = local.node.server_key
421 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
430 eg: set up listening ports
432 raise NotImplementedError
440 raise NotImplementedError
446 raise NotImplementedError
455 class TunProtoUDP(TunProtoBase):
456 def __init__(self, local, peer, home_path, key, listening):
457 super(TunProtoUDP, self).__init__(local, peer, home_path, key)
458 self.listening = listening
464 self.async_launch('udp', False, ("-u",str(self.port)))
472 def launch(self, check_proto='udp', listen=False, extra_args=None):
473 if extra_args is None:
474 extra_args = ("-u",str(self.port))
475 super(TunProtoUDP, self).launch(check_proto, listen, extra_args)
477 class TunProtoFD(TunProtoBase):
478 def __init__(self, local, peer, home_path, key, listening):
479 super(TunProtoFD, self).__init__(local, peer, home_path, key)
480 self.listening = listening
486 self.async_launch('fd', False)
494 def launch(self, check_proto='fd', listen=False, extra_args=[]):
495 super(TunProtoFD, self).launch(check_proto, listen, extra_args)
497 class TunProtoTCP(TunProtoBase):
498 def __init__(self, local, peer, home_path, key, listening):
499 super(TunProtoTCP, self).__init__(local, peer, home_path, key)
500 self.listening = listening
504 self.async_launch('tcp', True)
507 if not self.listening:
508 # make sure our peer is ready
510 if peer and peer.peer_proto_impl:
511 peer.peer_proto_impl.async_launch_wait()
513 if not self._started:
514 self.async_launch('tcp', False)
524 def launch(self, check_proto='tcp', listen=None, extra_args=[]):
526 listen = self.listening
527 super(TunProtoTCP, self).launch(check_proto, listen, extra_args)
529 class TapProtoUDP(TunProtoUDP):
530 def __init__(self, local, peer, home_path, key, listening):
531 super(TapProtoUDP, self).__init__(local, peer, home_path, key, listening)
534 class TapProtoTCP(TunProtoTCP):
535 def __init__(self, local, peer, home_path, key, listening):
536 super(TapProtoTCP, self).__init__(local, peer, home_path, key, listening)
539 class TapProtoFD(TunProtoFD):
540 def __init__(self, local, peer, home_path, key, listening):
541 super(TapProtoFD, self).__init__(local, peer, home_path, key, listening)