2 # -*- coding: utf-8 -*-
12 from nepi.util import server
14 class TunProtoBase(object):
15 def __init__(self, local, peer, home_path, key):
16 # Weak references, since ifaces do have a reference to the
17 # tunneling protocol implementation - we don't want strong
18 # circular references.
19 self.peer = weakref.ref(peer)
20 self.local = weakref.ref(local)
26 self.home_path = home_path
37 raise RuntimeError, "Lost reference to peering interfaces before launching"
39 raise RuntimeError, "Unconnected TUN - missing node"
41 # Make sure all the paths are created where
42 # they have to be created for deployment
43 cmd = "mkdir -p %s" % (server.shell_escape(self.home_path),)
44 (out,err),proc = server.popen_ssh_command(
46 host = local.node.hostname,
48 user = local.node.slicename,
50 ident_key = local.node.ident_path,
51 server_key = local.node.server_key
55 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
58 def _install_scripts(self):
62 raise RuntimeError, "Lost reference to peering interfaces before launching"
64 raise RuntimeError, "Unconnected TUN - missing node"
66 # Install the tun_connect script and tunalloc utility
68 os.path.join(os.path.dirname(__file__), 'scripts', 'tun_connect.py'),
69 os.path.join(os.path.dirname(__file__), 'scripts', 'tunalloc.c'),
72 local.node.slicename, local.node.hostname,
73 os.path.join(self.home_path,'.'),)
74 (out,err),proc = server.popen_scp(
77 ident_key = local.node.ident_path,
78 server_key = local.node.server_key
82 raise RuntimeError, "Failed upload TUN connect script %r: %s %s" % (source, out,err,)
85 "cd %(home)s && gcc -shared tunalloc.c -o tunalloc.so"
87 "wget -q -c -O python-passfd-src.tar.gz %(passfd_url)s && "
88 "mkdir -p python-passfd && "
89 "cd python-passfd && "
90 "tar xzf ../python-passfd-src.tar.gz --strip-components=1 && "
91 "python setup.py build && "
92 "python setup.py install --install-lib .. "
94 if local.tun_proto == "fd" else ""
97 'home' : server.shell_escape(self.home_path),
98 'passfd_url' : "http://yans.pl.sophia.inria.fr/code/hgwebdir.cgi/python-passfd/archive/2a6472c64c87.tar.gz",
100 (out,err),proc = server.popen_ssh_command(
102 host = local.node.hostname,
104 user = local.node.slicename,
106 ident_key = local.node.ident_path,
107 server_key = local.node.server_key
111 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
113 def launch(self, check_proto, listen, extra_args=[]):
117 if not peer or not local:
118 raise RuntimeError, "Lost reference to peering interfaces before launching"
120 peer_port = peer.tun_port
121 peer_addr = peer.tun_addr
122 peer_proto= peer.tun_proto
124 local_port = self.port
125 local_cap = local.capture
126 local_addr = local.address
127 local_mask = local.netprefix
128 local_snat = local.snat
129 local_txq = local.txqueuelen
131 if check_proto != peer_proto:
132 raise RuntimeError, "Peering protocol mismatch: %s != %s" % (check_proto, peer_proto)
134 if not listen and (not peer_port or not peer_addr):
135 raise RuntimeError, "Misconfigured peer: %s" % (peer,)
137 if listen and (not local_port or not local_addr or not local_mask):
138 raise RuntimeError, "Misconfigured TUN: %s" % (local,)
140 args = ["python", "tun_connect.py",
141 "-m", str(self.mode),
142 "-A", str(local_addr),
143 "-M", str(local_mask)]
145 if check_proto == 'fd':
146 passfd_arg = str(peer_addr)
147 if passfd_arg.startswith('\x00'):
148 # cannot shell_encode null characters :(
149 passfd_arg = "base64:"+base64.b64encode(passfd_arg)
151 passfd_arg = '$HOME/'+server.shell_escape(passfd_arg)
153 "--pass-fd", passfd_arg
157 "-p", str(local_port if listen else peer_port),
164 args.extend(("-Q",str(local_txq)))
166 args.extend(map(str,extra_args))
167 if not listen and check_proto != 'fd':
168 args.append(str(peer_addr))
171 self._install_scripts()
173 # Start process in a "daemonized" way, using nohup and heavy
174 # stdin/out redirection to avoid connection issues
175 (out,err),proc = rspawn.remote_spawn(
179 home = self.home_path,
181 stdout = 'capture' if local_cap else '/dev/null',
182 stderr = rspawn.STDOUT,
185 host = local.node.hostname,
187 user = local.node.slicename,
189 ident_key = local.node.ident_path,
190 server_key = local.node.server_key
194 raise RuntimeError, "Failed to set up TUN: %s %s" % (out,err,)
198 def async_launch(self, check_proto, listen, extra_args=[]):
199 if not self._launcher:
200 self._launcher = threading.Thread(
201 target = self.launch,
202 args = (check_proto, listen, extra_args))
203 self._launcher.start()
205 def async_launch_wait(self):
206 if not self._started:
208 self._launcher.join()
209 if not self._started:
210 raise RuntimeError, "Failed to launch TUN forwarder"
218 raise RuntimeError, "Lost reference to local interface"
221 # NOTE: wait a bit for the pidfile to be created
222 if self._started and not self._pid or not self._ppid:
223 pidtuple = rspawn.remote_check_pid(
224 os.path.join(self.home_path,'pid'),
225 host = local.node.hostname,
227 user = local.node.slicename,
229 ident_key = local.node.ident_path,
230 server_key = local.node.server_key
234 self._pid, self._ppid = pidtuple
240 raise RuntimeError, "Lost reference to local interface"
243 if not self._started:
244 return rspawn.NOT_STARTED
245 elif not self._pid or not self._ppid:
246 return rspawn.NOT_STARTED
248 status = rspawn.remote_status(
249 self._pid, self._ppid,
250 host = local.node.hostname,
252 user = local.node.slicename,
254 ident_key = local.node.ident_path
262 raise RuntimeError, "Lost reference to local interface"
264 status = self.status()
265 if status == rspawn.RUNNING:
266 # kill by ppid+pid - SIGTERM first, then try SIGKILL
268 self._pid, self._ppid,
269 host = local.node.hostname,
271 user = local.node.slicename,
273 ident_key = local.node.ident_path,
274 server_key = local.node.server_key,
278 def sync_trace(self, local_dir, whichtrace):
279 if whichtrace != 'packets':
287 local_path = os.path.join(local_dir, 'capture')
289 # create parent local folders
290 proc = subprocess.Popen(
291 ["mkdir", "-p", os.path.dirname(local_path)],
292 stdout = open("/dev/null","w"),
293 stdin = open("/dev/null","r"))
296 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
299 (out,err),proc = server.popen_scp(
300 '%s@%s:%s' % (local.node.slicename, local.node.hostname,
301 os.path.join(self.home_path, 'capture')),
305 ident_key = local.node.ident_path,
306 server_key = local.node.server_key
310 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
319 eg: set up listening ports
321 raise NotImplementedError
329 raise NotImplementedError
335 raise NotImplementedError
338 class TunProtoUDP(TunProtoBase):
339 def __init__(self, local, peer, home_path, key, listening):
340 super(TunProtoUDP, self).__init__(local, peer, home_path, key)
341 self.listening = listening
347 self.async_launch('udp', False, ("-u",str(self.port)))
352 class TunProtoFD(TunProtoBase):
353 def __init__(self, local, peer, home_path, key, listening):
354 super(TunProtoFD, self).__init__(local, peer, home_path, key)
355 self.listening = listening
361 self.async_launch('fd', False)
366 class TunProtoTCP(TunProtoBase):
367 def __init__(self, local, peer, home_path, key, listening):
368 super(TunProtoTCP, self).__init__(local, peer, home_path, key)
369 self.listening = listening
373 self.async_launch('tcp', True)
376 if not self.listening:
377 # make sure our peer is ready
379 if peer and peer.peer_proto_impl:
380 peer.peer_proto_impl.async_launch_wait()
382 if not self._started:
383 self.launch('tcp', False)
385 # make sure WE are ready
386 self.async_launch_wait()
393 class TapProtoUDP(TunProtoUDP):
394 def __init__(self, local, peer, home_path, key, listening):
395 super(TapProtoUDP, self).__init__(local, peer, home_path, key, listening)
398 class TapProtoTCP(TunProtoTCP):
399 def __init__(self, local, peer, home_path, key, listening):
400 super(TapProtoTCP, self).__init__(local, peer, home_path, key, listening)
403 class TapProtoFD(TunProtoFD):
404 def __init__(self, local, peer, home_path, key, listening):
405 super(TapProtoFD, self).__init__(local, peer, home_path, key, listening)