Merge non-handshake stuff
[nepi.git] / src / nepi / testbeds / planetlab / tunproto.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 import weakref
5 import os
6 import os.path
7 import rspawn
8 import subprocess
9 import threading
10 import base64
11 import time
12 import re
13 import sys
14 import logging
15
16 from nepi.util import server
17
18 class TunProtoBase(object):
19     def __init__(self, local, peer, home_path, key):
20         # Weak references, since ifaces do have a reference to the
21         # tunneling protocol implementation - we don't want strong
22         # circular references.
23         self.peer = weakref.ref(peer)
24         self.local = weakref.ref(local)
25         
26         self.port = 15000
27         self.mode = 'pl-tun'
28         self.key = key
29         self.cross_slice = False
30         
31         self.home_path = home_path
32         
33         self._launcher = None
34         self._started = False
35         self._started_listening = False
36         self._starting = False
37         self._pid = None
38         self._ppid = None
39         self._if_name = None
40
41         # Logging
42         self._logger = logging.getLogger('nepi.testbeds.planetlab')
43     
44     def __str__(self):
45         local = self.local()
46         if local:
47             return '<%s for %s>' % (self.__class__.__name__, local)
48         else:
49             return super(TunProtoBase,self).__str__()
50
51     def _make_home(self):
52         local = self.local()
53         
54         if not local:
55             raise RuntimeError, "Lost reference to peering interfaces before launching"
56         if not local.node:
57             raise RuntimeError, "Unconnected TUN - missing node"
58         
59         # Make sure all the paths are created where 
60         # they have to be created for deployment
61         # Also remove pidfile, if there is one.
62         # Old pidfiles from previous runs can be troublesome.
63         cmd = "mkdir -p %(home)s ; rm -f %(home)s/pid %(home)s/*.so" % {
64             'home' : server.shell_escape(self.home_path)
65         }
66         (out,err),proc = server.eintr_retry(server.popen_ssh_command)(
67             cmd,
68             host = local.node.hostname,
69             port = None,
70             user = local.node.slicename,
71             agent = None,
72             ident_key = local.node.ident_path,
73             server_key = local.node.server_key,
74             timeout = 60,
75             retry = 3
76             )
77         
78         if proc.wait():
79             raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
80         
81     
82     def _install_scripts(self):
83         local = self.local()
84         
85         if not local:
86             raise RuntimeError, "Lost reference to peering interfaces before launching"
87         if not local.node:
88             raise RuntimeError, "Unconnected TUN - missing node"
89         
90         # Install the tun_connect script and tunalloc utility
91         from nepi.util import tunchannel
92         from nepi.util import ipaddr2
93         sources = [
94             os.path.join(os.path.dirname(__file__), 'scripts', 'tun_connect.py'),
95             os.path.join(os.path.dirname(__file__), 'scripts', 'tunalloc.c'),
96             re.sub(r"([.]py)[co]$", r'\1', tunchannel.__file__, 1), # pyc/o files are version-specific
97             re.sub(r"([.]py)[co]$", r'\1', ipaddr2.__file__, 1), # pyc/o files are version-specific
98         ]
99         if local.filter_module:
100             filter_sources = filter(bool,map(str.strip,local.filter_module.module.split()))
101             filter_module = filter_sources[0]
102             
103             # Translate paths to builtin sources
104             for i,source in enumerate(filter_sources):
105                 if not os.path.exists(source):
106                     # Um... try the builtin folder
107                     source = os.path.join(os.path.dirname(__file__), "scripts", source)
108                     if os.path.exists(source):
109                         # Yep... replace
110                         filter_sources[i] = source
111
112             sources.extend(set(filter_sources))
113                 
114         else:
115             filter_module = None
116             filter_sources = None
117         dest = "%s@%s:%s" % (
118             local.node.slicename, local.node.hostname, 
119             os.path.join(self.home_path,'.'),)
120         (out,err),proc = server.eintr_retry(server.popen_scp)(
121             sources,
122             dest,
123             ident_key = local.node.ident_path,
124             server_key = local.node.server_key
125             )
126     
127         if proc.wait():
128             raise RuntimeError, "Failed upload TUN connect script %r: %s %s" % (sources, out,err,)
129         
130         # Make sure all dependencies are satisfied
131         local.node.wait_dependencies()
132
133         cmd = ( (
134             "cd %(home)s && "
135             "gcc -fPIC -shared tunalloc.c -o tunalloc.so && "
136             
137             "wget -q -c -O python-iovec-src.tar.gz %(iovec_url)s && "
138             "mkdir -p python-iovec && "
139             "cd python-iovec && "
140             "tar xzf ../python-iovec-src.tar.gz --strip-components=1 && "
141             "python setup.py build && "
142             "python setup.py install --install-lib .. && "
143             "cd .. "
144             
145             + ( " && "
146                 "gcc -fPIC -shared %(sources)s -o %(module)s.so " % {
147                    'module' : os.path.basename(filter_module).rsplit('.',1)[0],
148                    'sources' : ' '.join(map(os.path.basename,filter_sources))
149                 }
150                 
151                 if filter_module is not None and filter_module.endswith('.c')
152                 else ""
153             )
154             
155             + ( " && "
156                 "wget -q -c -O python-passfd-src.tar.gz %(passfd_url)s && "
157                 "mkdir -p python-passfd && "
158                 "cd python-passfd && "
159                 "tar xzf ../python-passfd-src.tar.gz --strip-components=1 && "
160                 "python setup.py build && "
161                 "python setup.py install --install-lib .. "
162                 
163                 if local.tun_proto == "fd" 
164                 else ""
165             ) 
166           )
167         % {
168             'home' : server.shell_escape(self.home_path),
169             'passfd_url' : "http://yans.pl.sophia.inria.fr/code/hgwebdir.cgi/python-passfd/archive/2a6472c64c87.tar.gz",
170             'iovec_url' : "http://yans.pl.sophia.inria.fr/code/hgwebdir.cgi/python-iovec/archive/tip.tar.gz",
171         } )
172         (out,err),proc = server.popen_ssh_command(
173             cmd,
174             host = local.node.hostname,
175             port = None,
176             user = local.node.slicename,
177             agent = None,
178             ident_key = local.node.ident_path,
179             server_key = local.node.server_key,
180             timeout = 300
181             )
182         
183         if proc.wait():
184             raise RuntimeError, "Failed to set up TUN forwarder: %s %s" % (out,err,)
185         
186     def launch(self, check_proto, listen, extra_args=[]):
187         if self._starting:
188             raise AssertionError, "Double start"
189         
190         self._starting = True
191         
192         peer = self.peer()
193         local = self.local()
194         
195         if not peer or not local:
196             raise RuntimeError, "Lost reference to peering interfaces before launching"
197         
198         peer_port = peer.tun_port
199         peer_addr = peer.tun_addr
200         peer_proto= peer.tun_proto
201         peer_cipher=peer.tun_cipher
202         
203         local_port = self.port
204         local_cap  = local.capture
205         local_addr = local.address
206         local_mask = local.netprefix
207         local_snat = local.snat
208         local_txq  = local.txqueuelen
209         local_p2p  = local.pointopoint
210         local_cipher=local.tun_cipher
211         local_mcast= local.multicast
212         local_bwlim= local.bwlimit
213         local_mcastfwd = local.multicast_forwarder
214         
215         if not local_p2p and hasattr(peer, 'address'):
216             local_p2p = peer.address
217
218         if check_proto != peer_proto:
219             raise RuntimeError, "Peering protocol mismatch: %s != %s" % (check_proto, peer_proto)
220         
221         if local_cipher != peer_cipher:
222             raise RuntimeError, "Peering cipher mismatch: %s != %s" % (local_cipher, peer_cipher)
223         
224         if not listen and ((peer_proto != 'fd' and not peer_port) or not peer_addr):
225             raise RuntimeError, "Misconfigured peer: %s" % (peer,)
226         
227         if listen and ((peer_proto != 'fd' and not local_port) or not local_addr or not local_mask):
228             raise RuntimeError, "Misconfigured TUN: %s" % (local,)
229
230         if check_proto == 'gre' and local_cipher.lower() != 'plain':
231             raise RuntimeError, "Misconfigured TUN: %s - GRE tunnels do not support encryption. Got %s, you MUST use PLAIN" % (local, local_cipher,)
232
233         if local.filter_module:
234             if check_proto not in ('udp', 'tcp'):
235                 raise RuntimeError, "Miscofnigured TUN: %s - filtered tunnels only work with udp or tcp links" % (local,)
236             filter_module = filter(bool,map(str.strip,local.filter_module.module.split()))
237             filter_module = os.path.join('.',os.path.basename(filter_module[0]))
238             if filter_module.endswith('.c'):
239                 filter_module = filter_module.rsplit('.',1)[0] + '.so'
240             filter_args = local.filter_module.args
241         else:
242             filter_module = None
243             filter_args = None
244         
245         args = ["python", "tun_connect.py", 
246             "-m", str(self.mode),
247             "-A", str(local_addr),
248             "-M", str(local_mask),
249             "-C", str(local_cipher),
250             ]
251         
252         if check_proto == 'fd':
253             passfd_arg = str(peer_addr)
254             if passfd_arg.startswith('\x00'):
255                 # cannot shell_encode null characters :(
256                 passfd_arg = "base64:"+base64.b64encode(passfd_arg)
257             else:
258                 passfd_arg = '$HOME/'+server.shell_escape(passfd_arg)
259             args.extend([
260                 "--pass-fd", passfd_arg
261             ])
262         elif check_proto == 'gre':
263             if self.cross_slice:
264                 args.extend([
265                     "-K", str(self.key.strip('='))
266                 ])
267         else:
268             args.extend([
269                 "-p", str(local_port if listen else peer_port),
270                 "-k", str(self.key)
271             ])
272         
273         if local_snat:
274             args.append("-S")
275         if local_p2p:
276             args.extend(("-P",str(local_p2p)))
277         if local_txq:
278             args.extend(("-Q",str(local_txq)))
279         if not local_cap:
280             args.append("-N")
281         elif local_cap == 'pcap':
282             args.extend(('-c','pcap'))
283         if local_bwlim:
284             args.extend(("-b",str(local_bwlim*1024)))
285         if extra_args:
286             args.extend(map(str,extra_args))
287         if not listen and check_proto != 'fd':
288             args.append(str(peer_addr))
289         if filter_module:
290             args.extend(("--filter", filter_module))
291         if filter_args:
292             args.extend(("--filter-args", filter_args))
293         if local_mcast and local_mcastfwd:
294             args.extend(("--multicast-forwarder", local_mcastfwd))
295
296         self._logger.info("Starting %s", self)
297         
298         self._make_home()
299         self._install_scripts()
300
301         # Start process in a "daemonized" way, using nohup and heavy
302         # stdin/out redirection to avoid connection issues
303         (out,err),proc = rspawn.remote_spawn(
304             " ".join(args),
305             
306             pidfile = './pid',
307             home = self.home_path,
308             stdin = '/dev/null',
309             stdout = 'capture',
310             stderr = rspawn.STDOUT,
311             sudo = True,
312             
313             host = local.node.hostname,
314             port = None,
315             user = local.node.slicename,
316             agent = None,
317             ident_key = local.node.ident_path,
318             server_key = local.node.server_key
319             )
320         
321         if proc.wait():
322             raise RuntimeError, "Failed to set up TUN: %s %s" % (out,err,)
323         
324         self._started = True
325     
326     def recover(self):
327         # Tunnel should be still running in its node
328         # Just check its pidfile and we're done
329         self._started = True
330         self._started_listening = True
331         self.checkpid()
332     
333     def _launch_and_wait(self, *p, **kw):
334         try:
335             self.__launch_and_wait(*p, **kw)
336         except:
337             if self._launcher:
338                 import sys
339                 self._launcher._exc.append(sys.exc_info())
340             else:
341                 raise
342             
343     def __launch_and_wait(self, *p, **kw):
344         local = self.local()
345         
346         self.launch(*p, **kw)
347         
348         # Wait for the process to be started
349         while self.status() == rspawn.NOT_STARTED:
350             time.sleep(1.0)
351         
352         # Wait for the connection to be established
353         retrytime = 2.0
354         for spin in xrange(30):
355             if self.status() != rspawn.RUNNING:
356                 self._logger.warn("FAILED TO CONNECT! %s", self)
357                 break
358             
359             # Connected?
360             (out,err),proc = server.eintr_retry(server.popen_ssh_command)(
361                 "cd %(home)s ; grep -a -c Connected capture" % dict(
362                     home = server.shell_escape(self.home_path)),
363                 host = local.node.hostname,
364                 port = None,
365                 user = local.node.slicename,
366                 agent = None,
367                 ident_key = local.node.ident_path,
368                 server_key = local.node.server_key,
369                 timeout = 60,
370                 err_on_timeout = False
371                 )
372             proc.wait()
373
374             if out.strip() == '1':
375                 break
376
377             # At least listening?
378             (out,err),proc = server.eintr_retry(server.popen_ssh_command)(
379                 "cd %(home)s ; grep -a -c Listening capture" % dict(
380                     home = server.shell_escape(self.home_path)),
381                 host = local.node.hostname,
382                 port = None,
383                 user = local.node.slicename,
384                 agent = None,
385                 ident_key = local.node.ident_path,
386                 server_key = local.node.server_key,
387                 timeout = 60,
388                 err_on_timeout = False
389                 )
390             proc.wait()
391
392             if out.strip() == '1':
393                 self._started_listening = True
394             
395             time.sleep(min(30.0, retrytime))
396             retrytime *= 1.1
397         else:
398             (out,err),proc = server.eintr_retry(server.popen_ssh_command)(
399                 "cat %(home)s/capture" % dict(
400                     home = server.shell_escape(self.home_path)),
401                 host = local.node.hostname,
402                 port = None,
403                 user = local.node.slicename,
404                 agent = None,
405                 ident_key = local.node.ident_path,
406                 server_key = local.node.server_key,
407                 timeout = 60,
408                 retry = 3,
409                 err_on_timeout = False
410                 )
411             proc.wait()
412
413             raise RuntimeError, "FAILED TO CONNECT %s: %s%s" % (self,out,err)
414     
415     @property
416     def if_name(self):
417         if not self._if_name:
418             # Inspect the trace to check the assigned iface
419             local = self.local()
420             if local:
421                 cmd = "cd %(home)s ; grep -a 'Using tun:' capture | head -1" % dict(
422                             home = server.shell_escape(self.home_path))
423                 for spin in xrange(30):
424                     (out,err),proc = server.eintr_retry(server.popen_ssh_command)(
425                         cmd,
426                         host = local.node.hostname,
427                         port = None,
428                         user = local.node.slicename,
429                         agent = None,
430                         ident_key = local.node.ident_path,
431                         server_key = local.node.server_key,
432                         timeout = 60,
433                         err_on_timeout = False
434                         )
435                     
436                     if proc.wait():
437                         self._logger.debug("if_name: failed cmd %s", cmd)
438                         time.sleep(1)
439                         continue
440                     
441                     out = out.strip()
442                     
443                     match = re.match(r"Using +tun: +([-a-zA-Z0-9]*).*",out)
444                     if match:
445                         self._if_name = match.group(1)
446                         break
447                     elif out:
448                         self._logger.debug("if_name: %r does not match expected pattern from cmd %s", out, cmd)
449                     else:
450                         self._logger.debug("if_name: empty output from cmd %s", cmd)
451                     time.sleep(1)
452                 else:
453                     self._logger.warn("if_name: Could not get interface name")
454         return self._if_name
455     
456     def if_alive(self):
457         name = self.if_name
458         if name:
459             local = self.local()
460             for i in xrange(30):
461                 (out,err),proc = server.eintr_retry(server.popen_ssh_command)(
462                     "ip show %s >/dev/null 2>&1 && echo ALIVE || echo DEAD" % (name,),
463                     host = local.node.hostname,
464                     port = None,
465                     user = local.node.slicename,
466                     agent = None,
467                     ident_key = local.node.ident_path,
468                     server_key = local.node.server_key,
469                     timeout = 60,
470                     err_on_timeout = False
471                     )
472                 
473                 if proc.wait():
474                     time.sleep(1)
475                     continue
476                 
477                 if out.strip() == 'DEAD':
478                     return False
479                 elif out.strip() == 'ALIVE':
480                     return True
481         return False
482     
483     def async_launch(self, check_proto, listen, extra_args=[]):
484         if not self._started and not self._launcher:
485             self._launcher = threading.Thread(
486                 target = self._launch_and_wait,
487                 args = (check_proto, listen, extra_args))
488             self._launcher._exc = []
489             self._launcher.start()
490     
491     def async_launch_wait(self):
492         if self._launcher:
493             self._launcher.join()
494
495             if self._launcher._exc:
496                 exctyp,exval,exctrace = self._launcher._exc[0]
497                 raise exctyp,exval,exctrace
498             elif not self._started:
499                 raise RuntimeError, "Failed to launch TUN forwarder"
500         elif not self._started:
501             self.launch()
502
503     def async_launch_wait_listening(self):
504         if self._launcher:
505             for x in xrange(180):
506                 if self._launcher._exc:
507                     exctyp,exval,exctrace = self._launcher._exc[0]
508                     raise exctyp,exval,exctrace
509                 elif self._started and self._started_listening:
510                     break
511                 time.sleep(1)
512         elif not self._started:
513             self.launch()
514
515     def checkpid(self):            
516         local = self.local()
517         
518         if not local:
519             raise RuntimeError, "Lost reference to local interface"
520         
521         # Get PID/PPID
522         # NOTE: wait a bit for the pidfile to be created
523         if self._started and not self._pid or not self._ppid:
524             pidtuple = rspawn.remote_check_pid(
525                 os.path.join(self.home_path,'pid'),
526                 host = local.node.hostname,
527                 port = None,
528                 user = local.node.slicename,
529                 agent = None,
530                 ident_key = local.node.ident_path,
531                 server_key = local.node.server_key
532                 )
533             
534             if pidtuple:
535                 self._pid, self._ppid = pidtuple
536     
537     def status(self):
538         local = self.local()
539         
540         if not local:
541             raise RuntimeError, "Lost reference to local interface"
542         
543         self.checkpid()
544         if not self._started:
545             return rspawn.NOT_STARTED
546         elif not self._pid or not self._ppid:
547             return rspawn.NOT_STARTED
548         else:
549             status = rspawn.remote_status(
550                 self._pid, self._ppid,
551                 host = local.node.hostname,
552                 port = None,
553                 user = local.node.slicename,
554                 agent = None,
555                 ident_key = local.node.ident_path,
556                 server_key = local.node.server_key
557                 )
558             return status
559     
560     def kill(self, nowait = True):
561         local = self.local()
562         
563         if not local:
564             raise RuntimeError, "Lost reference to local interface"
565         
566         status = self.status()
567         if status == rspawn.RUNNING:
568             self._logger.info("Stopping %s", self)
569             
570             # kill by ppid+pid - SIGTERM first, then try SIGKILL
571             rspawn.remote_kill(
572                 self._pid, self._ppid,
573                 host = local.node.hostname,
574                 port = None,
575                 user = local.node.slicename,
576                 agent = None,
577                 ident_key = local.node.ident_path,
578                 server_key = local.node.server_key,
579                 sudo = True,
580                 nowait = nowait
581                 )
582     
583     def waitkill(self):
584         interval = 1.0
585         for i in xrange(30):
586             status = self.status()
587             if status != rspawn.RUNNING:
588                 self._logger.info("Stopped %s", self)
589                 break
590             time.sleep(interval)
591             interval = min(30.0, interval * 1.1)
592         else:
593             self.kill(nowait=False)
594
595         if self.if_name:
596             for i in xrange(30):
597                 if not self.if_alive():
598                     self._logger.info("Device down %s", self)
599                     break
600                 time.sleep(interval)
601                 interval = min(30.0, interval * 1.1)
602     
603     _TRACEMAP = {
604         # tracename : (remotename, localname)
605         'packets' : ('capture','capture'),
606         'pcap' : ('pcap','capture.pcap'),
607     }
608     
609     def remote_trace_path(self, whichtrace, tracemap = None):
610         tracemap = self._TRACEMAP if not tracemap else tracemap
611         
612         
613         if whichtrace not in tracemap:
614             return None
615         
616         return os.path.join(self.home_path, tracemap[whichtrace][1])
617         
618     def sync_trace(self, local_dir, whichtrace, tracemap = None):
619         tracemap = self._TRACEMAP if not tracemap else tracemap
620         
621         if whichtrace not in tracemap:
622             return None
623         
624         local = self.local()
625         
626         if not local:
627             return None
628         
629         local_path = os.path.join(local_dir, tracemap[whichtrace][1])
630         
631         # create parent local folders
632         if os.path.dirname(local_path):
633             proc = subprocess.Popen(
634                 ["mkdir", "-p", os.path.dirname(local_path)],
635                 stdout = open("/dev/null","w"),
636                 stdin = open("/dev/null","r"))
637
638             if proc.wait():
639                 raise RuntimeError, "Failed to synchronize trace"
640         
641         # sync files
642         (out,err),proc = server.popen_scp(
643             '%s@%s:%s' % (local.node.slicename, local.node.hostname, 
644                 os.path.join(self.home_path, tracemap[whichtrace][0])),
645             local_path,
646             port = None,
647             agent = None,
648             ident_key = local.node.ident_path,
649             server_key = local.node.server_key
650             )
651         
652         if proc.wait():
653             raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
654         
655         return local_path
656         
657         
658     def prepare(self):
659         """
660         First-phase setup
661         
662         eg: set up listening ports
663         """
664         raise NotImplementedError
665     
666     def setup(self):
667         """
668         Second-phase setup
669         
670         eg: connect to peer
671         """
672         raise NotImplementedError
673     
674     def shutdown(self):
675         """
676         Cleanup
677         """
678         raise NotImplementedError
679     
680     def destroy(self):
681         """
682         Second-phase cleanup
683         """
684         pass
685         
686
687 class TunProtoUDP(TunProtoBase):
688     def __init__(self, local, peer, home_path, key, listening):
689         super(TunProtoUDP, self).__init__(local, peer, home_path, key)
690         self.listening = listening
691     
692     def prepare(self):
693         pass
694     
695     def setup(self):
696         self.async_launch('udp', False, ("-u",str(self.port)))
697     
698     def shutdown(self):
699         self.kill()
700
701     def destroy(self):
702         self.waitkill()
703
704     def launch(self, check_proto='udp', listen=False, extra_args=None):
705         if extra_args is None:
706             extra_args = ("-u",str(self.port))
707         super(TunProtoUDP, self).launch(check_proto, listen, extra_args)
708
709 class TunProtoFD(TunProtoBase):
710     def __init__(self, local, peer, home_path, key, listening):
711         super(TunProtoFD, self).__init__(local, peer, home_path, key)
712         self.listening = listening
713     
714     def prepare(self):
715         pass
716     
717     def setup(self):
718         self.async_launch('fd', False)
719     
720     def shutdown(self):
721         self.kill()
722
723     def destroy(self):
724         self.waitkill()
725
726     def launch(self, check_proto='fd', listen=False, extra_args=[]):
727         super(TunProtoFD, self).launch(check_proto, listen, extra_args)
728
729 class TunProtoGRE(TunProtoBase):
730     def __init__(self, local, peer, home_path, key, listening):
731         super(TunProtoGRE, self).__init__(local, peer, home_path, key)
732         self.listening = listening
733         self.mode = 'pl-gre-ip'
734     
735     def prepare(self):
736         pass
737     
738     def setup(self):
739         self.async_launch('gre', False)
740     
741     def shutdown(self):
742         self.kill()
743
744     def destroy(self):
745         self.waitkill()
746
747     def launch(self, check_proto='gre', listen=False, extra_args=[]):
748         super(TunProtoGRE, self).launch(check_proto, listen, extra_args)
749
750 class TunProtoTCP(TunProtoBase):
751     def __init__(self, local, peer, home_path, key, listening):
752         super(TunProtoTCP, self).__init__(local, peer, home_path, key)
753         self.listening = listening
754     
755     def prepare(self):
756         if self.listening:
757             self.async_launch('tcp', True)
758     
759     def setup(self):
760         if not self.listening:
761             # make sure our peer is ready
762             peer = self.peer()
763             if peer and peer.peer_proto_impl:
764                 peer.peer_proto_impl.async_launch_wait_listening()
765             
766             if not self._started:
767                 self.async_launch('tcp', False)
768         
769         self.checkpid()
770     
771     def shutdown(self):
772         self.kill()
773
774     def destroy(self):
775         self.waitkill()
776
777     def launch(self, check_proto='tcp', listen=None, extra_args=[]):
778         if listen is None:
779             listen = self.listening
780         super(TunProtoTCP, self).launch(check_proto, listen, extra_args)
781
782 class TapProtoUDP(TunProtoUDP):
783     def __init__(self, local, peer, home_path, key, listening):
784         super(TapProtoUDP, self).__init__(local, peer, home_path, key, listening)
785         self.mode = 'pl-tap'
786
787 class TapProtoTCP(TunProtoTCP):
788     def __init__(self, local, peer, home_path, key, listening):
789         super(TapProtoTCP, self).__init__(local, peer, home_path, key, listening)
790         self.mode = 'pl-tap'
791
792 class TapProtoFD(TunProtoFD):
793     def __init__(self, local, peer, home_path, key, listening):
794         super(TapProtoFD, self).__init__(local, peer, home_path, key, listening)
795         self.mode = 'pl-tap'
796
797 class TapProtoGRE(TunProtoGRE):
798     def __init__(self, local, peer, home_path, key, listening):
799         super(TapProtoGRE, self).__init__(local, peer, home_path, key, listening)
800         self.mode = 'pl-gre-eth'
801
802
803
804 TUN_PROTO_MAP = {
805     'tcp' : TunProtoTCP,
806     'udp' : TunProtoUDP,
807     'fd'  : TunProtoFD,
808     'gre' : TunProtoGRE,
809 }
810
811 TAP_PROTO_MAP = {
812     'tcp' : TapProtoTCP,
813     'udp' : TapProtoUDP,
814     'fd'  : TapProtoFD,
815     'gre' : TapProtoGRE,
816 }
817
818