2 # -*- coding: utf-8 -*-
11 from nepi.util import server
13 class TunProtoBase(object):
14 def __init__(self, local, peer, home_path, key):
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)
25 self.home_path = home_path
36 raise RuntimeError, "Lost reference to peering interfaces before launching"
38 raise RuntimeError, "Unconnected TUN - missing node"
40 # Make sure all the paths are created where
41 # they have to be created for deployment
42 cmd = "mkdir -p %s" % (server.shell_escape(self.home_path),)
43 (out,err),proc = server.popen_ssh_command(
45 host = local.node.hostname,
47 user = local.node.slicename,
49 ident_key = local.node.ident_path,
50 server_key = local.node.server_key
54 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
57 def _install_scripts(self):
61 raise RuntimeError, "Lost reference to peering interfaces before launching"
63 raise RuntimeError, "Unconnected TUN - missing node"
65 # Install the tun_connect script and tunalloc utility
67 os.path.join(os.path.dirname(__file__), 'scripts', 'tun_connect.py'),
68 os.path.join(os.path.dirname(__file__), 'scripts', 'tunalloc.c'),
71 local.node.slicename, local.node.hostname,
72 os.path.join(self.home_path,'.'),)
73 (out,err),proc = server.popen_scp(
76 ident_key = local.node.ident_path,
77 server_key = local.node.server_key
81 raise RuntimeError, "Failed upload TUN connect script %r: %s %s" % (source, out,err,)
83 cmd = "cd %s && gcc -shared tunalloc.c -o tunalloc.so" % (server.shell_escape(self.home_path),)
84 (out,err),proc = server.popen_ssh_command(
86 host = local.node.hostname,
88 user = local.node.slicename,
90 ident_key = local.node.ident_path,
91 server_key = local.node.server_key
95 raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
97 def launch(self, check_proto, listen, extra_args=[]):
101 if not peer or not local:
102 raise RuntimeError, "Lost reference to peering interfaces before launching"
104 peer_port = peer.tun_port
105 peer_addr = peer.tun_addr
106 peer_proto= peer.tun_proto
108 local_port = self.port
109 local_cap = local.capture
110 local_addr = local.address
111 local_mask = local.netprefix
112 local_snat = local.snat
113 local_txq = local.txqueuelen
115 if check_proto != peer_proto:
116 raise RuntimeError, "Peering protocol mismatch: %s != %s" % (check_proto, peer_proto)
118 if not listen and (not peer_port or not peer_addr):
119 raise RuntimeError, "Misconfigured peer: %s" % (peer,)
121 if listen and (not local_port or not local_addr or not local_mask):
122 raise RuntimeError, "Misconfigured TUN: %s" % (local,)
124 args = ["python", "tun_connect.py",
125 "-m", str(self.mode),
126 "-p", str(local_port if listen else peer_port),
127 "-A", str(local_addr),
128 "-M", str(local_mask),
134 args.extend(("-Q",str(local_txq)))
136 args.extend(map(str,extra_args))
138 args.append(str(peer_addr))
141 self._install_scripts()
143 # Start process in a "daemonized" way, using nohup and heavy
144 # stdin/out redirection to avoid connection issues
145 (out,err),proc = rspawn.remote_spawn(
149 home = self.home_path,
151 stdout = 'capture' if local_cap else '/dev/null',
152 stderr = rspawn.STDOUT,
155 host = local.node.hostname,
157 user = local.node.slicename,
159 ident_key = local.node.ident_path,
160 server_key = local.node.server_key
164 raise RuntimeError, "Failed to set up TUN: %s %s" % (out,err,)
168 def async_launch(self, check_proto, listen, extra_args=[]):
169 if not self._launcher:
170 self._launcher = threading.Thread(
171 target = self.launch,
172 args = (check_proto, listen, extra_args))
173 self._launcher.start()
175 def async_launch_wait(self):
176 if not self._started:
178 self._launcher.join()
179 if not self._started:
180 raise RuntimeError, "Failed to launch TUN forwarder"
188 raise RuntimeError, "Lost reference to local interface"
191 # NOTE: wait a bit for the pidfile to be created
192 if self._started and not self._pid or not self._ppid:
193 pidtuple = rspawn.remote_check_pid(
194 os.path.join(self.home_path,'pid'),
195 host = local.node.hostname,
197 user = local.node.slicename,
199 ident_key = local.node.ident_path,
200 server_key = local.node.server_key
204 self._pid, self._ppid = pidtuple
210 raise RuntimeError, "Lost reference to local interface"
213 if not self._started:
214 return rspawn.NOT_STARTED
215 elif not self._pid or not self._ppid:
216 return rspawn.NOT_STARTED
218 status = rspawn.remote_status(
219 self._pid, self._ppid,
220 host = local.node.hostname,
222 user = local.node.slicename,
224 ident_key = local.node.ident_path
232 raise RuntimeError, "Lost reference to local interface"
234 status = self.status()
235 if status == rspawn.RUNNING:
236 # kill by ppid+pid - SIGTERM first, then try SIGKILL
238 self._pid, self._ppid,
239 host = local.node.hostname,
241 user = local.node.slicename,
243 ident_key = local.node.ident_path,
244 server_key = local.node.server_key,
248 def sync_trace(self, local_dir, whichtrace):
249 if whichtrace != 'packets':
257 local_path = os.path.join(local_dir, 'capture')
259 # create parent local folders
260 proc = subprocess.Popen(
261 ["mkdir", "-p", os.path.dirname(local_path)],
262 stdout = open("/dev/null","w"),
263 stdin = open("/dev/null","r"))
266 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
269 (out,err),proc = server.popen_scp(
270 '%s@%s:%s' % (local.node.slicename, local.node.hostname,
271 os.path.join(self.home_path, 'capture')),
275 ident_key = local.node.ident_path,
276 server_key = local.node.server_key
280 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
289 eg: set up listening ports
291 raise NotImplementedError
299 raise NotImplementedError
305 raise NotImplementedError
308 class TunProtoUDP(TunProtoBase):
309 def __init__(self, local, peer, home_path, key, listening):
310 super(TunProtoUDP, self).__init__(local, peer, home_path, key)
311 self.listening = listening
317 self.async_launch('udp', False, ("-u",str(self.port)))
322 class TunProtoTCP(TunProtoBase):
323 def __init__(self, local, peer, home_path, key, listening):
324 super(TunProtoTCP, self).__init__(local, peer, home_path, key)
325 self.listening = listening
329 self.async_launch('tcp', True)
332 if not self.listening:
333 # make sure our peer is ready
335 if peer and peer.peer_proto_impl:
336 peer.peer_proto_impl.async_launch_wait()
338 self.launch('tcp', False)
340 # make sure WE are ready
341 self.async_launch_wait()
348 class TapProtoUDP(TunProtoUDP):
349 def __init__(self, local, peer, home_path, key, listening):
350 super(TapProtoUDP, self).__init__(local, peer, home_path, key, listening)
353 class TapProtoTCP(TunProtoTCP):
354 def __init__(self, local, peer, home_path, key, listening):
355 super(TapProtoTCP, self).__init__(local, peer, home_path, key, listening)