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