2 # -*- coding: utf-8 -*-
13 from nepi.util import server
15 class TunProtoBase(object):
16 def __init__(self, local, peer, home_path, key):
17 # Weak references, since ifaces do have a reference to the
18 # tunneling protocol implementation - we don't want strong
19 # circular references.
20 self.peer = weakref.ref(peer)
21 self.local = weakref.ref(local)
27 self.home_path = home_path
38 raise RuntimeError, "Lost reference to peering interfaces before launching"
40 raise RuntimeError, "Unconnected TUN - missing node"
42 # Make sure all the paths are created where
43 # they have to be created for deployment
44 cmd = "mkdir -p %s" % (server.shell_escape(self.home_path),)
45 (out,err),proc = server.popen_ssh_command(
47 host = local.node.hostname,
49 user = local.node.slicename,
51 ident_key = local.node.ident_path,
52 server_key = local.node.server_key
56 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
59 def _install_scripts(self):
63 raise RuntimeError, "Lost reference to peering interfaces before launching"
65 raise RuntimeError, "Unconnected TUN - missing node"
67 # Install the tun_connect script and tunalloc utility
69 os.path.join(os.path.dirname(__file__), 'scripts', 'tun_connect.py'),
70 os.path.join(os.path.dirname(__file__), 'scripts', 'tunalloc.c'),
73 local.node.slicename, local.node.hostname,
74 os.path.join(self.home_path,'.'),)
75 (out,err),proc = server.popen_scp(
78 ident_key = local.node.ident_path,
79 server_key = local.node.server_key
83 raise RuntimeError, "Failed upload TUN connect script %r: %s %s" % (source, out,err,)
86 "cd %(home)s && gcc -fPIC -shared tunalloc.c -o tunalloc.so"
88 "wget -q -c -O python-passfd-src.tar.gz %(passfd_url)s && "
89 "mkdir -p python-passfd && "
90 "cd python-passfd && "
91 "tar xzf ../python-passfd-src.tar.gz --strip-components=1 && "
92 "python setup.py build && "
93 "python setup.py install --install-lib .. "
95 if local.tun_proto == "fd" else ""
98 'home' : server.shell_escape(self.home_path),
99 'passfd_url' : "http://yans.pl.sophia.inria.fr/code/hgwebdir.cgi/python-passfd/archive/2a6472c64c87.tar.gz",
101 (out,err),proc = server.popen_ssh_command(
103 host = local.node.hostname,
105 user = local.node.slicename,
107 ident_key = local.node.ident_path,
108 server_key = local.node.server_key
112 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
114 def launch(self, check_proto, listen, extra_args=[]):
118 if not peer or not local:
119 raise RuntimeError, "Lost reference to peering interfaces before launching"
121 peer_port = peer.tun_port
122 peer_addr = peer.tun_addr
123 peer_proto= peer.tun_proto
125 local_port = self.port
126 local_cap = local.capture
127 local_addr = local.address
128 local_mask = local.netprefix
129 local_snat = local.snat
130 local_txq = local.txqueuelen
132 if check_proto != peer_proto:
133 raise RuntimeError, "Peering protocol mismatch: %s != %s" % (check_proto, peer_proto)
135 if not listen and (not peer_port or not peer_addr):
136 raise RuntimeError, "Misconfigured peer: %s" % (peer,)
138 if listen and (not local_port or not local_addr or not local_mask):
139 raise RuntimeError, "Misconfigured TUN: %s" % (local,)
141 args = ["python", "tun_connect.py",
142 "-m", str(self.mode),
143 "-A", str(local_addr),
144 "-M", str(local_mask)]
146 if check_proto == 'fd':
147 passfd_arg = str(peer_addr)
148 if passfd_arg.startswith('\x00'):
149 # cannot shell_encode null characters :(
150 passfd_arg = "base64:"+base64.b64encode(passfd_arg)
152 passfd_arg = '$HOME/'+server.shell_escape(passfd_arg)
154 "--pass-fd", passfd_arg
158 "-p", str(local_port if listen else peer_port),
165 args.extend(("-Q",str(local_txq)))
167 args.extend(map(str,extra_args))
168 if not listen and check_proto != 'fd':
169 args.append(str(peer_addr))
172 self._install_scripts()
174 # Start process in a "daemonized" way, using nohup and heavy
175 # stdin/out redirection to avoid connection issues
176 (out,err),proc = rspawn.remote_spawn(
180 home = self.home_path,
182 stdout = 'capture' if local_cap else '/dev/null',
183 stderr = rspawn.STDOUT,
186 host = local.node.hostname,
188 user = local.node.slicename,
190 ident_key = local.node.ident_path,
191 server_key = local.node.server_key
195 raise RuntimeError, "Failed to set up TUN: %s %s" % (out,err,)
199 def _launch_and_wait(self, *p, **kw):
202 self.launch(*p, **kw)
204 # Wait for the process to be started
205 while self.status() == rspawn.NOT_STARTED:
208 # Wait for the connection to be established
210 for spin in xrange(30):
211 if self.status() != rspawn.RUNNING:
214 (out,err),proc = server.popen_ssh_command(
215 "cd %(home)s ; grep -c Connected capture" % dict(
216 home = server.shell_escape(self.home_path)),
217 host = local.node.hostname,
219 user = local.node.slicename,
221 ident_key = local.node.ident_path,
222 server_key = local.node.server_key
228 if out.strip() != '0':
233 def async_launch(self, check_proto, listen, extra_args=[]):
234 if not self._launcher:
235 self._launcher = threading.Thread(
236 target = self._launch_and_wait,
237 args = (check_proto, listen, extra_args))
238 self._launcher.start()
240 def async_launch_wait(self):
242 self._launcher.join()
243 if not self._started:
244 raise RuntimeError, "Failed to launch TUN forwarder"
245 elif not self._started:
252 raise RuntimeError, "Lost reference to local interface"
255 # NOTE: wait a bit for the pidfile to be created
256 if self._started and not self._pid or not self._ppid:
257 pidtuple = rspawn.remote_check_pid(
258 os.path.join(self.home_path,'pid'),
259 host = local.node.hostname,
261 user = local.node.slicename,
263 ident_key = local.node.ident_path,
264 server_key = local.node.server_key
268 self._pid, self._ppid = pidtuple
274 raise RuntimeError, "Lost reference to local interface"
277 if not self._started:
278 return rspawn.NOT_STARTED
279 elif not self._pid or not self._ppid:
280 return rspawn.NOT_STARTED
282 status = rspawn.remote_status(
283 self._pid, self._ppid,
284 host = local.node.hostname,
286 user = local.node.slicename,
288 ident_key = local.node.ident_path
296 raise RuntimeError, "Lost reference to local interface"
298 status = self.status()
299 if status == rspawn.RUNNING:
300 # kill by ppid+pid - SIGTERM first, then try SIGKILL
302 self._pid, self._ppid,
303 host = local.node.hostname,
305 user = local.node.slicename,
307 ident_key = local.node.ident_path,
308 server_key = local.node.server_key,
312 def sync_trace(self, local_dir, whichtrace):
313 if whichtrace != 'packets':
321 local_path = os.path.join(local_dir, 'capture')
323 # create parent local folders
324 proc = subprocess.Popen(
325 ["mkdir", "-p", os.path.dirname(local_path)],
326 stdout = open("/dev/null","w"),
327 stdin = open("/dev/null","r"))
330 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
333 (out,err),proc = server.popen_scp(
334 '%s@%s:%s' % (local.node.slicename, local.node.hostname,
335 os.path.join(self.home_path, 'capture')),
339 ident_key = local.node.ident_path,
340 server_key = local.node.server_key
344 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
353 eg: set up listening ports
355 raise NotImplementedError
363 raise NotImplementedError
369 raise NotImplementedError
372 class TunProtoUDP(TunProtoBase):
373 def __init__(self, local, peer, home_path, key, listening):
374 super(TunProtoUDP, self).__init__(local, peer, home_path, key)
375 self.listening = listening
381 self.async_launch('udp', False, ("-u",str(self.port)))
386 class TunProtoFD(TunProtoBase):
387 def __init__(self, local, peer, home_path, key, listening):
388 super(TunProtoFD, self).__init__(local, peer, home_path, key)
389 self.listening = listening
395 self.async_launch('fd', False)
400 class TunProtoTCP(TunProtoBase):
401 def __init__(self, local, peer, home_path, key, listening):
402 super(TunProtoTCP, self).__init__(local, peer, home_path, key)
403 self.listening = listening
407 self.async_launch('tcp', True)
410 if not self.listening:
411 # make sure our peer is ready
413 if peer and peer.peer_proto_impl:
414 peer.peer_proto_impl.async_launch_wait()
416 if not self._started:
417 self.launch('tcp', False)
419 # make sure WE are ready
420 self.async_launch_wait()
427 class TapProtoUDP(TunProtoUDP):
428 def __init__(self, local, peer, home_path, key, listening):
429 super(TapProtoUDP, self).__init__(local, peer, home_path, key, listening)
432 class TapProtoTCP(TunProtoTCP):
433 def __init__(self, local, peer, home_path, key, listening):
434 super(TapProtoTCP, self).__init__(local, peer, home_path, key, listening)
437 class TapProtoFD(TunProtoFD):
438 def __init__(self, local, peer, home_path, key, listening):
439 super(TapProtoFD, self).__init__(local, peer, home_path, key, listening)