2 # -*- coding: utf-8 -*-
11 from nepi.util import server
13 class TunProtoBase(object):
14 def __init__(self, local, peer, home_path):
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)
24 self.home_path = home_path
35 raise RuntimeError, "Lost reference to peering interfaces before launching"
37 raise RuntimeError, "Unconnected TUN - missing node"
39 # Make sure all the paths are created where
40 # they have to be created for deployment
41 cmd = "mkdir -p %s" % (server.shell_escape(self.home_path),)
42 (out,err),proc = server.popen_ssh_command(
44 host = local.node.hostname,
46 user = local.node.slicename,
48 ident_key = local.node.ident_path,
49 server_key = local.node.server_key
53 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
56 def _install_scripts(self):
60 raise RuntimeError, "Lost reference to peering interfaces before launching"
62 raise RuntimeError, "Unconnected TUN - missing node"
64 # Install the tun_connect script and tunalloc utility
66 os.path.join(os.path.dirname(__file__), 'scripts', 'tun_connect.py'),
67 os.path.join(os.path.dirname(__file__), 'scripts', 'tunalloc.c'),
70 local.node.slicename, local.node.hostname,
71 os.path.join(self.home_path,'.'),)
72 (out,err),proc = server.popen_scp(
75 ident_key = local.node.ident_path,
76 server_key = local.node.server_key
80 raise RuntimeError, "Failed upload TUN connect script %r: %s %s" % (source, out,err,)
82 cmd = "cd %s && gcc -shared tunalloc.c -o tunalloc.so" % (server.shell_escape(self.home_path),)
83 (out,err),proc = server.popen_ssh_command(
85 host = local.node.hostname,
87 user = local.node.slicename,
89 ident_key = local.node.ident_path,
90 server_key = local.node.server_key
94 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
96 def launch(self, check_proto, listen, extra_args=[]):
100 if not peer or not local:
101 raise RuntimeError, "Lost reference to peering interfaces before launching"
103 peer_port = peer.tun_port
104 peer_addr = peer.tun_addr
105 peer_proto= peer.tun_proto
107 local_port = self.port
108 local_cap = local.capture
109 local_addr = local.address
110 local_mask = local.netprefix
111 local_snat = local.snat
112 local_txq = local.txqueuelen
114 if check_proto != peer_proto:
115 raise RuntimeError, "Peering protocol mismatch: %s != %s" % (check_proto, peer_proto)
117 if not listen and (not peer_port or not peer_addr):
118 raise RuntimeError, "Misconfigured peer: %s" % (peer,)
120 if listen and (not local_port or not local_addr or not local_mask):
121 raise RuntimeError, "Misconfigured TUN: %s" % (local,)
123 args = ["python", "tun_connect.py",
124 "-m", str(self.mode),
125 "-p", str(local_port if listen else peer_port),
126 "-A", str(local_addr),
127 "-M", str(local_mask)]
132 args.extend(("-Q",str(local_txq)))
134 args.extend(map(str,extra_args))
136 args.append(str(peer_addr))
139 self._install_scripts()
141 # Start process in a "daemonized" way, using nohup and heavy
142 # stdin/out redirection to avoid connection issues
143 (out,err),proc = rspawn.remote_spawn(
147 home = self.home_path,
149 stdout = 'capture' if local_cap else '/dev/null',
150 stderr = rspawn.STDOUT,
153 host = local.node.hostname,
155 user = local.node.slicename,
157 ident_key = local.node.ident_path,
158 server_key = local.node.server_key
162 raise RuntimeError, "Failed to set up TUN: %s %s" % (out,err,)
166 def async_launch(self, check_proto, listen, extra_args=[]):
167 if not self._launcher:
168 self._launcher = threading.Thread(
169 target = self.launch,
170 args = (check_proto, listen, extra_args))
171 self._launcher.start()
173 def async_launch_wait(self):
174 if not self._started:
176 self._launcher.join()
177 if not self._started:
178 raise RuntimeError, "Failed to launch TUN forwarder"
186 raise RuntimeError, "Lost reference to local interface"
189 # NOTE: wait a bit for the pidfile to be created
190 if self._started and not self._pid or not self._ppid:
191 pidtuple = rspawn.remote_check_pid(
192 os.path.join(self.home_path,'pid'),
193 host = local.node.hostname,
195 user = local.node.slicename,
197 ident_key = local.node.ident_path,
198 server_key = local.node.server_key
202 self._pid, self._ppid = pidtuple
208 raise RuntimeError, "Lost reference to local interface"
211 if not self._started:
212 return rspawn.NOT_STARTED
213 elif not self._pid or not self._ppid:
214 return rspawn.NOT_STARTED
216 status = rspawn.remote_status(
217 self._pid, self._ppid,
218 host = local.node.hostname,
220 user = local.node.slicename,
222 ident_key = local.node.ident_path
230 raise RuntimeError, "Lost reference to local interface"
232 status = self.status()
233 if status == rspawn.RUNNING:
234 # kill by ppid+pid - SIGTERM first, then try SIGKILL
236 self._pid, self._ppid,
237 host = local.node.hostname,
239 user = local.node.slicename,
241 ident_key = local.node.ident_path,
242 server_key = local.node.server_key,
246 def sync_trace(self, local_dir, whichtrace):
247 if whichtrace != 'packets':
255 local_path = os.path.join(local_dir, 'capture')
257 # create parent local folders
258 proc = subprocess.Popen(
259 ["mkdir", "-p", os.path.dirname(local_path)],
260 stdout = open("/dev/null","w"),
261 stdin = open("/dev/null","r"))
264 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
267 (out,err),proc = server.popen_scp(
268 '%s@%s:%s' % (local.node.slicename, local.node.hostname,
269 os.path.join(self.home_path, 'capture')),
273 ident_key = local.node.ident_path,
274 server_key = local.node.server_key
278 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
287 eg: set up listening ports
289 raise NotImplementedError
297 raise NotImplementedError
303 raise NotImplementedError
306 class TunProtoUDP(TunProtoBase):
307 def __init__(self, local, peer, home_path, listening):
308 super(TunProtoTCP, self).__init__(local, peer, home_path)
309 self.listening = listening
315 self.launch_async('udp', False, ("-U",))
320 class TunProtoTCP(TunProtoBase):
321 def __init__(self, local, peer, home_path, listening):
322 super(TunProtoTCP, self).__init__(local, peer, home_path)
323 self.listening = listening
327 self.async_launch('tcp', True)
330 if not self.listening:
331 # make sure our peer is ready
333 if peer and peer.peer_proto_impl:
334 peer.peer_proto_impl.async_launch_wait()
336 self.launch('tcp', False)
338 # make sure WE are ready
339 self.async_launch_wait()