25 tun_path = '/dev/net/tun'
26 hostaddr = socket.gethostbyname(socket.gethostname())
28 usage = "usage: %prog [options]"
30 parser = optparse.OptionParser(usage=usage)
33 "-i", "--iface", dest="tun_name", metavar="DEVICE",
35 help = "TUN/TAP interface to tap into")
37 "-d", "--tun-path", dest="tun_path", metavar="PATH",
38 default = "/dev/net/tun",
39 help = "TUN/TAP device file path or file descriptor number")
41 "-p", "--peer-port", dest="peer_port", metavar="PEER_PORT", type="int",
43 help = "Remote TCP/UDP port to connect to.")
45 "--pass-fd", dest="pass_fd", metavar="UNIX_SOCKET",
47 help = "Path to a unix-domain socket to pass the TUN file descriptor to. "
48 "If given, all other connectivity options are ignored, tun_connect will "
49 "simply wait to be killed after passing the file descriptor, and it will be "
50 "the receiver's responsability to handle the tunneling.")
52 "-m", "--mode", dest="mode", metavar="MODE",
55 "Set mode. One of none, tun, tap, pl-tun, pl-tap, pl-gre-ip, pl-gre-eth. In any mode except none, a TUN/TAP will be created "
56 "by using the proper interface (tunctl for tun/tap, /vsys/fd_tuntap.control for pl-tun/pl-tap), "
57 "and it will be brought up (with ifconfig for tun/tap, with /vsys/vif_up for pl-tun/pl-tap). You have "
58 "to specify an VIF_ADDRESS and VIF_MASK in any case (except for none).")
60 "-t", "--protocol", dest="protocol", metavar="PROTOCOL",
63 "Set protocol. One of tcp, udp, fd, gre. In any mode except none, a TUN/TAP will be created.")
65 "-A", "--vif-address", dest="vif_addr", metavar="VIF_ADDRESS",
68 "See mode. This specifies the VIF_ADDRESS, "
69 "the IP address of the virtual interface.")
71 "-M", "--vif-mask", dest="vif_mask", type="int", metavar="VIF_MASK",
74 "See mode. This specifies the VIF_MASK, "
75 "a number indicating the network type (ie: 24 for a C-class network).")
77 "-P", "--port", dest="port", type="int", metavar="PORT",
80 "This specifies the LOCAL_PORT. This will be the local bind port for UDP/TCP.")
82 "-S", "--vif-snat", dest="vif_snat",
83 action = "store_true",
85 help = "See mode. This specifies whether SNAT will be enabled for the virtual interface. " )
87 "-Z", "--vif-pointopoint", dest="vif_pointopoint", metavar="DST_ADDR",
90 "See mode. This specifies the remote endpoint's virtual address, "
91 "for point-to-point routing configuration. "
92 "Not supported by PlanetLab" )
94 "-Q", "--vif-txqueuelen", dest="vif_txqueuelen", metavar="SIZE", type="int",
97 "See mode. This specifies the interface's transmission queue length. " )
99 "-b", "--bwlimit", dest="bwlimit", metavar="BYTESPERSECOND", type="int",
102 "This specifies the interface's emulated bandwidth in bytes per second." )
104 "-a", "--peer-address", dest="peer_addr", metavar="PEER_ADDRESS",
107 "This specifies the PEER_ADDRESS, "
108 "the IP address of the remote interface.")
110 "-k", "--key", dest="cipher_key", metavar="KEY",
113 "Specify a symmetric encryption key with which to protect packets across "
114 "the tunnel. python-crypto must be installed on the system." )
116 "-K", "--gre-key", dest="gre_key", metavar="KEY", type="int",
119 "Specify a demultiplexing 32-bit numeric key for GRE." )
121 "-C", "--cipher", dest="cipher", metavar="CIPHER",
123 help = "One of PLAIN, AES, Blowfish, DES, DES3. " )
125 "-N", "--no-capture", dest="no_capture",
126 action = "store_true",
128 help = "If specified, packets won't be logged to standard output "
129 "(default is to log them to standard output). " )
131 "-c", "--pcap-capture", dest="pcap_capture", metavar="FILE",
133 help = "If specified, packets won't be logged to standard output, "
134 "but dumped to a pcap-formatted trace in the specified file. " )
136 "--multicast", dest="multicast",
137 action = "store_true",
139 help = "If specified, multicast packets will be forwarded and IGMP "
140 "join/leave packets will be generated. Routing information "
141 "must be sent to the mroute unix socket, in a format identical "
142 "to that of the kernel's MRT ioctls, prefixed with 32-bit IOCTL "
143 "code and 32-bit data length." )
145 "--filter", dest="filter_module", metavar="PATH",
147 help = "If specified, it should be either a .py or .so module. "
148 "It will be loaded, and all incoming and outgoing packets "
149 "will be routed through it. The filter will not be responsible "
150 "for buffering, packet queueing is performed in tun_connect "
151 "already, so it should not concern itself with it. It should "
152 "not, however, block in one direction if the other is congested.\n"
154 "Modules are expected to have the following methods:\n"
156 "\t\tIf arguments are given, this method will be called with the\n"
157 "\t\tgiven arguments (as keyword args in python modules, or a single\n"
158 "\t\tstring in c modules).\n"
159 "\taccept_packet(packet, direction):\n"
160 "\t\tDecide whether to drop the packet. Direction is 0 for packets "
161 "coming from the local side to the remote, and 1 is for packets "
162 "coming from the remote side to the local. Return a boolean, "
163 "true if the packet is not to be dropped.\n"
165 "\t\tInitializes a filtering pipe (filter_run). It should "
166 "return two file descriptors to use as a bidirectional "
167 "pipe: local and remote. 'local' is where packets from the "
168 "local side will be written to. After filtering, those packets "
169 "should be written to 'remote', where tun_connect will read "
170 "from, and it will forward them to the remote peer. "
171 "Packets from the remote peer will be written to 'remote', "
172 "where the filter is expected to read from, and eventually "
173 "forward them to the local side. If the file descriptors are "
174 "not nonblocking, they will be set to nonblocking. So it's "
175 "better to set them from the start like that.\n"
176 "\tfilter_run(local, remote):\n"
177 "\t\tIf filter_init is provided, it will be called repeatedly, "
178 "in a separate thread until the process is killed. It should "
179 "sleep at most for a second.\n"
180 "\tfilter_close(local, remote):\n"
181 "\t\tCalled then the process is killed, if filter_init was provided. "
182 "It should, among other things, close the file descriptors.\n"
184 "Python modules are expected to return a tuple in filter_init, "
185 "either of file descriptors or file objects, while native ones "
186 "will receive two int*.\n"
188 "Python modules can additionally contain a custom queue class "
189 "that will replace the FIFO used by default. The class should "
190 "be named 'queueclass' and contain an interface compatible with "
191 "collections.deque. That is, indexing (especiall for q[0]), "
192 "bool(q), popleft, appendleft, pop (right), append (right), "
193 "len(q) and clear. When using a custom queue, queue size will "
194 "have no effect, pass an effective queue size to the module "
195 "by using filter_args" )
197 "--filter-args", dest="filter_args", metavar="FILE",
199 help = "If specified, packets won't be logged to standard output, "
200 "but dumped to a pcap-formatted trace in the specified file. " )
202 (options,args) = parser.parse_args(sys.argv[1:])
208 'blowfish' : 'Blowfish',
210 }[options.cipher.lower()]
212 ETH_P_ALL = 0x00000003
213 ETH_P_IP = 0x00000800
214 TUNSETIFF = 0x400454ca
215 IFF_NO_PI = 0x00001000
218 IFF_VNET_HDR = 0x00004000
219 TUN_PKT_STRIP = 0x00000001
220 IFHWADDRLEN = 0x00000006
221 IFNAMSIZ = 0x00000010
222 IFREQ_SZ = 0x00000028
223 FIONREAD = 0x0000541b
225 class MulticastThread(threading.Thread):
226 def __init__(self, *p, **kw):
227 super(MulticastThread, self).__init__(*p, **kw)
228 self.igmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IGMP)
229 self.igmp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF,
230 socket.inet_aton(options.vif_addr) )
231 self.igmp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
232 self.igmp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
237 devnull = open('/dev/null','r+b')
238 maddr_re = re.compile(r"\s*inet\s*(\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3})\s*")
240 lastfullrefresh = time.time()
241 while not self._stop:
242 # Get current subscriptions
243 proc = subprocess.Popen(['ip','maddr','show',tun_name],
244 stdout = subprocess.PIPE,
245 stderr = subprocess.STDOUT,
248 for line in proc.stdout:
249 match = maddr_re.match(line)
251 new_maddr.add(match.group(1))
254 # Every now and then, send a full report
256 report_new = new_maddr
257 if (now - lastfullrefresh) <= 30.0:
258 report_new = report_new - cur_maddr
260 lastfullrefresh = now
262 # Report subscriptions
263 for grp in report_new:
264 igmpp = ipaddr2.ipigmp(
265 options.vif_addr, '224.0.0.2', 1, 0x16, 0, grp,
267 self.igmp_socket.sendto(igmpp, 0, ('224.0.0.2',0))
270 for grp in cur_maddr - new_maddr:
271 igmpp = ipaddr2.ipigmp(
272 options.vif_addr, '224.0.0.2', 1, 0x17, 0, grp,
274 self.igmp_socket.sendto(igmpp, 0, ('224.0.0.2',0))
276 cur_maddr = new_maddr
284 class HostLock(object):
285 # This class is used as a lock to prevent concurrency issues with more
286 # than one instance of netns running in the same machine. Both in
287 # different processes or different threads.
289 processcond = threading.Condition()
291 def __init__(self, lockfile):
292 processcond = self.__class__.processcond
294 processcond.acquire()
297 while self.__class__.taken:
299 self.__class__.taken = True
301 processcond.release()
303 self.lockfile = lockfile
307 fcntl.flock(self.lockfile, fcntl.LOCK_EX)
309 except (OSError, IOError), e:
310 if e.args[0] != os.errno.EINTR:
314 processcond = self.__class__.processcond
316 processcond.acquire()
318 if not self.lockfile.closed:
319 fcntl.flock(self.lockfile, fcntl.LOCK_UN)
322 self.__class__.taken = False
325 processcond.release()
328 return x+'\x00'*(IFNAMSIZ-len(x))
330 def ifreq(iface, flags):
332 # char[IFNAMSIZ] : interface name
335 ifreq = ifnam(iface)+struct.pack("H",flags);
336 ifreq += '\x00' * (len(ifreq)-IFREQ_SZ)
339 def tunopen(tun_path, tun_name):
340 if tun_path.isdigit():
342 print >>sys.stderr, "Using tun:", tun_name, "fd", tun_path
343 tun = os.fdopen(int(tun_path), 'r+b', 0)
346 print >>sys.stderr, "Using tun:", tun_name, "at", tun_path
347 tun = open(tun_path, 'r+b', 0)
349 # bind file descriptor to the interface
350 fcntl.ioctl(tun.fileno(), TUNSETIFF, ifreq(tun_name, IFF_NO_PI|IFF_TUN))
354 def tunclose(tun_path, tun_name, tun):
355 if tun_path and tun_path.isdigit():
357 os.close(int(tun_path))
363 def noopen(tun_path, tun_name):
364 print >>sys.stderr, "Using tun:", tun_name
366 def noclose(tun_path, tun_name, tun):
369 def tuntap_alloc(kind, tun_path, tun_name):
375 args.append(tun_name)
376 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
377 out,err = proc.communicate()
379 raise RuntimeError, "Could not allocate %s device" % (kind,)
381 match = re.search(r"Set '(?P<dev>(?:tun|tap)[0-9]*)' persistent and owned by .*", out, re.I)
383 raise RuntimeError, "Could not allocate %s device - tunctl said: %s" % (kind, out)
385 tun_name = match.group("dev")
386 print >>sys.stderr, "Allocated %s device: %s" % (kind, tun_name)
388 return tun_path, tun_name
390 def tuntap_dealloc(tun_path, tun_name):
391 args = ["tunctl", "-d", tun_name]
392 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
393 out,err = proc.communicate()
395 print >> sys.stderr, "WARNING: error deallocating %s device" % (tun_name,)
397 def nmask_to_dot_notation(mask):
398 mask = hex(((1 << mask) - 1) << (32 - mask)) # 24 -> 0xFFFFFF00
399 mask = mask[2:] # strip 0x
400 mask = mask.decode("hex") # to bytes
401 mask = '.'.join(map(str,map(ord,mask))) # to 255.255.255.0
404 def vif_start(tun_path, tun_name):
405 args = ["ifconfig", tun_name, options.vif_addr,
406 "netmask", nmask_to_dot_notation(options.vif_mask),
408 if options.vif_pointopoint:
409 args.extend(["pointopoint",options.vif_pointopoint])
410 if options.vif_txqueuelen is not None:
411 args.extend(["txqueuelen",str(options.vif_txqueuelen)])
413 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
414 out,err = proc.communicate()
416 raise RuntimeError, "Error starting virtual interface"
419 # set up SNAT using iptables
420 # TODO: stop vif on error.
421 # Not so necessary since deallocating the tun/tap device
422 # will forcibly stop it, but it would be tidier
423 args = [ "iptables", "-t", "nat", "-A", "POSTROUTING",
424 "-s", "%s/%d" % (options.vif_addr, options.vif_mask),
426 "--to-source", hostaddr, "--random" ]
427 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
428 out,err = proc.communicate()
430 raise RuntimeError, "Error setting up SNAT"
432 def vif_stop(tun_path, tun_name):
434 # set up SNAT using iptables
435 args = [ "iptables", "-t", "nat", "-D", "POSTROUTING",
436 "-s", "%s/%d" % (options.vif_addr, options.vif_mask),
438 "--to-source", hostaddr, "--random" ]
439 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
440 out,err = proc.communicate()
442 args = ["ifconfig", tun_name, "down"]
443 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
444 out,err = proc.communicate()
446 print >>sys.stderr, "WARNING: error stopping virtual interface"
449 def pl_tuntap_alloc(kind, tun_path, tun_name):
450 tunalloc_so = ctypes.cdll.LoadLibrary("./tunalloc.so")
451 c_tun_name = ctypes.c_char_p("\x00"*IFNAMSIZ) # the string will be mutated!
452 kind = {"tun":IFF_TUN,
454 fd = tunalloc_so.tun_alloc(kind, c_tun_name)
455 name = c_tun_name.value
458 _name_reservation = None
459 def pl_tuntap_namealloc(kind, tun_path, tun_name):
460 global _name_reservation
462 lockfile = open("/tmp/nepi-tun-connect.lock", "a")
463 lock = HostLock(lockfile)
465 # We need to do this, fd_tuntap is the only one who can
466 # tell us our slice id (this script runs as root, so no uid),
467 # and the pattern of device names accepted by vsys scripts
468 tunalloc_so = ctypes.cdll.LoadLibrary("./tunalloc.so")
469 c_tun_name = ctypes.c_char_p("\x00"*IFNAMSIZ) # the string will be mutated!
470 nkind= {"tun":IFF_TUN,
472 fd = tunalloc_so.tun_alloc(nkind, c_tun_name)
473 name = c_tun_name.value
476 base = name[:name.index('-')+1]
477 existing = set(map(str.strip,os.popen("ip a | grep -o '%s[0-9]*'" % (base,)).read().strip().split('\n')))
479 for i in xrange(9000,10000):
481 if name not in existing:
484 raise RuntimeError, "Could not assign interface name"
486 _name_reservation = lock
490 def pl_vif_start(tun_path, tun_name):
491 global _name_reservation
495 out.append(stdout.read())
499 # Serialize access to vsys
500 lockfile = open("/tmp/nepi-tun-connect.lock", "a")
501 lock = _name_reservation or HostLock(lockfile)
502 _name_reservation = None
504 stdin = open("/vsys/vif_up.in","w")
505 stdout = open("/vsys/vif_up.out","r")
507 t = threading.Thread(target=outreader)
510 stdin.write(tun_name+"\n")
511 stdin.write(options.vif_addr+"\n")
512 stdin.write(str(options.vif_mask)+"\n")
514 stdin.write("snat=1\n")
515 if options.vif_pointopoint:
516 stdin.write("pointopoint=%s\n" % (options.vif_pointopoint,))
517 if options.vif_txqueuelen is not None:
518 stdin.write("txqueuelen=%d\n" % (options.vif_txqueuelen,))
519 if options.mode.startswith('pl-gre'):
520 stdin.write("gre=%d\n" % (options.gre_key,))
521 stdin.write("remote=%s\n" % (options.peer_addr,))
527 print >>sys.stderr, out
531 def pl_vif_stop(tun_path, tun_name):
534 out.append(stdout.read())
537 if options.mode.startswith('pl-gre'):
542 for i in xrange(lim):
543 ifaces = set(map(str.strip,os.popen("ip a | grep -o '%s'" % (tun_name,)).read().strip().split('\n')))
544 if tun_name in ifaces:
549 # Serialize access to vsys
550 lockfile = open("/tmp/nepi-tun-connect.lock", "a")
551 lock = HostLock(lockfile)
553 stdin = open("/vsys/vif_down.in","w")
554 stdout = open("/vsys/vif_down.out","r")
556 t = threading.Thread(target=outreader)
559 stdin.write(tun_name+"\n")
565 print >>sys.stderr, out
570 def tun_fwd(tun, remote, reconnect = None, accept_local = None, accept_remote = None, slowlocal = True, bwlimit = None):
573 tunqueue = options.vif_txqueuelen or 1000
576 # in PL mode, we cannot strip PI structs
577 # so we'll have to handle them
578 tunchannel.tun_fwd(tun, remote,
579 with_pi = options.mode.startswith('pl-'),
580 ether_mode = tun_name.startswith('tap'),
581 cipher_key = options.cipher_key,
582 udp = options.protocol == 'udp',
583 TERMINATE = TERMINATE,
585 reconnect = reconnect,
587 tunkqueue = tunkqueue,
588 cipher = options.cipher,
589 accept_local = accept_local,
590 accept_remote = accept_remote,
591 queueclass = queueclass,
592 slowlocal = slowlocal,
598 nop = lambda tun_path, tun_name : (tun_path, tun_name)
600 'none' : dict(alloc=nop,
601 tunopen=tunopen, tunclose=tunclose,
605 'tun' : dict(alloc=functools.partial(tuntap_alloc, "tun"),
606 tunopen=tunopen, tunclose=tunclose,
607 dealloc=tuntap_dealloc,
610 'tap' : dict(alloc=functools.partial(tuntap_alloc, "tap"),
611 tunopen=tunopen, tunclose=tunclose,
612 dealloc=tuntap_dealloc,
615 'pl-tun' : dict(alloc=functools.partial(pl_tuntap_alloc, "tun"),
616 tunopen=tunopen, tunclose=tunclose,
620 'pl-tap' : dict(alloc=functools.partial(pl_tuntap_alloc, "tap"),
621 tunopen=tunopen, tunclose=tunclose,
625 'pl-gre-ip' : dict(alloc=functools.partial(pl_tuntap_namealloc, "tun"),
626 tunopen=noopen, tunclose=tunclose,
630 'pl-gre-eth': dict(alloc=functools.partial(pl_tuntap_namealloc, "tap"),
631 tunopen=noopen, tunclose=noclose,
637 tun_path = options.tun_path
638 tun_name = options.tun_name
640 modeinfo = MODEINFO[options.mode]
642 # Try to load filter module
644 if options.filter_module:
645 print >>sys.stderr, "Loading module", options.filter_module, "with args", options.filter_args
646 if options.filter_module.endswith('.py'):
647 sys.path.append(os.path.dirname(options.filter_module))
648 filter_module = __import__(os.path.basename(options.filter_module).rsplit('.',1)[0])
649 if options.filter_args:
651 filter_args = dict(map(lambda x:x.split('=',1),options.filter_args.split(',')))
652 filter_module.init(**filter_args)
655 elif options.filter_module.endswith('.so'):
656 filter_module = ctypes.cdll.LoadLibrary(options.filter_module)
657 if options.filter_args:
659 filter_module.init(options.filter_args)
663 accept_packet = filter_module.accept_packet
664 print >>sys.stderr, "Installing packet filter (accept_packet)"
669 queueclass = filter_module.queueclass
670 print >>sys.stderr, "Installing custom queue"
675 _filter_init = filter_module.filter_init
676 filter_run = filter_module.filter_run
677 filter_close = filter_module.filter_close
680 filter_local = ctypes.c_int(0)
681 filter_remote = ctypes.c_int(0)
682 _filter_init(filter_local, filter_remote)
683 return filter_local, filter_remote
685 print >>sys.stderr, "Installing packet filter (stream filter)"
697 # be careful to roll back stuff on exceptions
698 tun_path, tun_name = modeinfo['alloc'](tun_path, tun_name)
700 modeinfo['start'](tun_path, tun_name)
702 tun = modeinfo['tunopen'](tun_path, tun_name)
704 modeinfo['stop'](tun_path, tun_name)
707 modeinfo['dealloc'](tun_path, tun_name)
711 # Trak SIGTERM, and set global termination flag instead of dying
713 def _finalize(sig,frame):
715 TERMINATE.append(None)
716 signal.signal(signal.SIGTERM, _finalize)
723 if options.protocol == 'fd':
724 if accept_packet or filter_init:
725 raise NotImplementedError, "--pass-fd and --filter are not compatible"
727 if options.pass_fd.startswith("base64:"):
728 options.pass_fd = base64.b64decode(
729 options.pass_fd[len("base64:"):])
730 options.pass_fd = os.path.expandvars(options.pass_fd)
732 print >>sys.stderr, "Sending FD to: %r" % (options.pass_fd,)
734 # send FD to whoever wants it
737 sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
741 raise OSError, "Killed"
743 sock.connect(options.pass_fd)
746 # wait a while, retry
747 print >>sys.stderr, "%s: Could not connect. Retrying in a sec..." % (time.strftime('%c'),)
748 time.sleep(min(30.0,retrydelay))
751 sock.connect(options.pass_fd)
752 passfd.sendfd(sock, tun.fileno(), '0')
755 def tun_fwd(tun, remote, **kw):
761 elif options.protocol == "gre":
762 if accept_packet or filter_init:
763 raise NotImplementedError, "--mode %s and --filter are not compatible" % (options.mode,)
766 def tun_fwd(tun, remote, **kw):
771 remote = options.peer_addr
772 elif options.protocol == "udp":
773 # connect to remote endpoint
774 if options.peer_addr and options.peer_port:
775 rsock = tunchannel.udp_establish(TERMINATE, hostaddr, options.port,
776 options.peer_addr, options.peer_port)
777 remote = os.fdopen(rsock.fileno(), 'r+b', 0)
779 print >>sys.stderr, "Error: need a remote endpoint in UDP mode"
780 raise AssertionError, "Error: need a remote endpoint in UDP mode"
781 elif options.protocol == "tcp":
782 # connect to remote endpoint
783 if options.peer_addr and options.peer_port:
784 rsock = tunchannel.tcp_establish(TERMINATE, hostaddr, options.port,
785 options.peer_addr, options.peer_port)
786 remote = os.fdopen(rsock.fileno(), 'r+b', 0)
788 print >>sys.stderr, "Error: need a remote endpoint in TCP mode"
789 raise AssertionError, "Error: need a remote endpoint in TCP mode"
791 msg = "Error: Invalid protocol %s" % options.protocol
792 print >>sys.stderr, msg
793 raise AssertionError, msg
796 filter_local, filter_remote = filter_init()
803 remote = filter_remote
806 filter_close(local, remote)
808 filter_thread = threading.Thread(target=filter_loop)
809 filter_thread.start()
811 print >>sys.stderr, "Connected"
813 if not options.no_capture:
814 # Launch a tcpdump subprocess, to capture and dump packets.
815 # Make sure to catch sigterm and kill the tcpdump as well
816 tcpdump = subprocess.Popen(
817 ["tcpdump","-l","-n","-i",tun_name, "-s", "4096"]
818 + ["-w",options.pcap_capture,"-U"] * bool(options.pcap_capture) )
820 # Try to give us high priority
824 # Ignore errors, we might not have enough privileges,
825 # or perhaps there is no os.nice support in the system
828 if options.multicast:
829 # Start multicast forwarding daemon
830 mcastthread = MulticastThread()
835 reconnect = reconnect,
836 accept_local = accept_packet,
837 accept_remote = accept_packet,
838 bwlimit = options.bwlimit,
843 # 1. Forward packets from tun to filter
844 # 2. Forward packets from remote to filter
846 # 1. needs TUN rate-limiting, while
847 # 2. needs reconnection
849 # 1. needs ONLY TUN-side acceptance checks, while
850 # 2. needs ONLY remote-side acceptance checks
851 if isinstance(filter_local, ctypes.c_int):
852 filter_local_fd = filter_local.value
854 filter_local_fd = filter_local
855 if isinstance(filter_remote, ctypes.c_int):
856 filter_remote_fd = filter_remote.value
858 filter_remote_fd = filter_remote
861 tun_fwd(tun, filter_local_fd,
862 accept_local = accept_packet,
866 tun_fwd(filter_remote_fd, remote,
867 reconnect = reconnect,
868 accept_remote = accept_packet,
869 bwlimit = options.bwlimit,
872 localthread = threading.Thread(target=localside)
873 remotethread = threading.Thread(target=remoteside)
881 print >>sys.stderr, "Shutting down..."
883 # In case sys.stderr is broken
886 # tidy shutdown in every case - swallow exceptions
887 TERMINATE.append(None)
903 os.kill(tcpdump.pid, signal.SIGTERM)
909 modeinfo['stop'](tun_path, tun_name)
911 traceback.print_exc()
914 modeinfo['tunclose'](tun_path, tun_name, tun)
916 traceback.print_exc()
919 modeinfo['dealloc'](tun_path, tun_name)
921 traceback.print_exc()
923 print >>sys.stderr, "TERMINATED GRACEFULLY"