31 tun_path = '/dev/net/tun'
32 hostaddr = socket.gethostbyname(socket.gethostname())
34 usage = "usage: %prog [options] <remote-endpoint>"
36 parser = optparse.OptionParser(usage=usage)
39 "-i", "--iface", dest="tun_name", metavar="DEVICE",
41 help = "TUN/TAP interface to tap into")
43 "-d", "--tun-path", dest="tun_path", metavar="PATH",
44 default = "/dev/net/tun",
45 help = "TUN/TAP device file path or file descriptor number")
47 "-p", "--port", dest="port", metavar="PORT", type="int",
49 help = "Peering TCP port to connect or listen to.")
51 "--pass-fd", dest="pass_fd", metavar="UNIX_SOCKET",
53 help = "Path to a unix-domain socket to pass the TUN file descriptor to. "
54 "If given, all other connectivity options are ignored, tun_connect will "
55 "simply wait to be killed after passing the file descriptor, and it will be "
56 "the receiver's responsability to handle the tunneling.")
59 "-m", "--mode", dest="mode", metavar="MODE",
62 "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 "
63 "by using the proper interface (tunctl for tun/tap, /vsys/fd_tuntap.control for pl-tun/pl-tap), "
64 "and it will be brought up (with ifconfig for tun/tap, with /vsys/vif_up for pl-tun/pl-tap). You have "
65 "to specify an VIF_ADDRESS and VIF_MASK in any case (except for none).")
67 "-A", "--vif-address", dest="vif_addr", metavar="VIF_ADDRESS",
70 "See mode. This specifies the VIF_ADDRESS, "
71 "the IP address of the virtual interface.")
73 "-M", "--vif-mask", dest="vif_mask", type="int", metavar="VIF_MASK",
76 "See mode. This specifies the VIF_MASK, "
77 "a number indicating the network type (ie: 24 for a C-class network).")
79 "-S", "--vif-snat", dest="vif_snat",
80 action = "store_true",
82 help = "See mode. This specifies whether SNAT will be enabled for the virtual interface. " )
84 "-P", "--vif-pointopoint", dest="vif_pointopoint", metavar="DST_ADDR",
87 "See mode. This specifies the remote endpoint's virtual address, "
88 "for point-to-point routing configuration. "
89 "Not supported by PlanetLab" )
91 "-Q", "--vif-txqueuelen", dest="vif_txqueuelen", metavar="SIZE", type="int",
94 "See mode. This specifies the interface's transmission queue length. " )
96 "-b", "--bwlimit", dest="bwlimit", metavar="BYTESPERSECOND", type="int",
99 "This specifies the interface's emulated bandwidth in bytes per second." )
101 "-u", "--udp", dest="udp", metavar="PORT", type="int",
104 "Bind to the specified UDP port locally, and send UDP datagrams to the "
105 "remote endpoint, creating a tunnel through UDP rather than TCP." )
107 "-k", "--key", dest="cipher_key", metavar="KEY",
110 "Specify a symmetric encryption key with which to protect packets across "
111 "the tunnel. python-crypto must be installed on the system." )
113 "-K", "--gre-key", dest="gre_key", metavar="KEY", type="string",
116 "Specify a demultiplexing 32-bit numeric key for GRE." )
118 "-C", "--cipher", dest="cipher", metavar="CIPHER",
120 help = "One of PLAIN, AES, Blowfish, DES, DES3. " )
122 "-N", "--no-capture", dest="no_capture",
123 action = "store_true",
125 help = "If specified, packets won't be logged to standard output "
126 "(default is to log them to standard output). " )
128 "-c", "--pcap-capture", dest="pcap_capture", metavar="FILE",
130 help = "If specified, packets won't be logged to standard output, "
131 "but dumped to a pcap-formatted trace in the specified file. " )
133 "--multicast", dest="multicast",
134 action = "store_true",
136 help = "If specified, multicast packets will be forwarded and IGMP "
137 "join/leave packets will be generated. Routing information "
138 "must be sent to the mroute unix socket, in a format identical "
139 "to that of the kernel's MRT ioctls, prefixed with 32-bit IOCTL "
140 "code and 32-bit data length." )
142 "--multicast-forwarder", dest="multicast_fwd",
144 help = "If specified, multicast packets will be forwarded to "
145 "the specified unix-domain socket. If the device uses ethernet "
146 "frames, ethernet headers will be stripped and IP packets "
147 "will be forwarded." )
149 "--filter", dest="filter_module", metavar="PATH",
151 help = "If specified, it should be either a .py or .so module. "
152 "It will be loaded, and all incoming and outgoing packets "
153 "will be routed through it. The filter will not be responsible "
154 "for buffering, packet queueing is performed in tun_connect "
155 "already, so it should not concern itself with it. It should "
156 "not, however, block in one direction if the other is congested.\n"
158 "Modules are expected to have the following methods:\n"
160 "\t\tIf arguments are given, this method will be called with the\n"
161 "\t\tgiven arguments (as keyword args in python modules, or a single\n"
162 "\t\tstring in c modules).\n"
163 "\taccept_packet(packet, direction):\n"
164 "\t\tDecide whether to drop the packet. Direction is 0 for packets "
165 "coming from the local side to the remote, and 1 is for packets "
166 "coming from the remote side to the local. Return a boolean, "
167 "true if the packet is not to be dropped.\n"
169 "\t\tInitializes a filtering pipe (filter_run). It should "
170 "return two file descriptors to use as a bidirectional "
171 "pipe: local and remote. 'local' is where packets from the "
172 "local side will be written to. After filtering, those packets "
173 "should be written to 'remote', where tun_connect will read "
174 "from, and it will forward them to the remote peer. "
175 "Packets from the remote peer will be written to 'remote', "
176 "where the filter is expected to read from, and eventually "
177 "forward them to the local side. If the file descriptors are "
178 "not nonblocking, they will be set to nonblocking. So it's "
179 "better to set them from the start like that.\n"
180 "\tfilter_run(local, remote):\n"
181 "\t\tIf filter_init is provided, it will be called repeatedly, "
182 "in a separate thread until the process is killed. It should "
183 "sleep at most for a second.\n"
184 "\tfilter_close(local, remote):\n"
185 "\t\tCalled then the process is killed, if filter_init was provided. "
186 "It should, among other things, close the file descriptors.\n"
188 "Python modules are expected to return a tuple in filter_init, "
189 "either of file descriptors or file objects, while native ones "
190 "will receive two int*.\n"
192 "Python modules can additionally contain a custom queue class "
193 "that will replace the FIFO used by default. The class should "
194 "be named 'queueclass' and contain an interface compatible with "
195 "collections.deque. That is, indexing (especiall for q[0]), "
196 "bool(q), popleft, appendleft, pop (right), append (right), "
197 "len(q) and clear. When using a custom queue, queue size will "
198 "have no effect, pass an effective queue size to the module "
199 "by using filter_args" )
201 "--filter-args", dest="filter_args", metavar="FILE",
203 help = "If specified, packets won't be logged to standard output, "
204 "but dumped to a pcap-formatted trace in the specified file. " )
206 (options, remaining_args) = parser.parse_args(sys.argv[1:])
212 'blowfish' : 'Blowfish',
214 }[options.cipher.lower()]
216 ETH_P_ALL = 0x00000003
217 ETH_P_IP = 0x00000800
218 TUNSETIFF = 0x400454ca
219 IFF_NO_PI = 0x00001000
222 IFF_VNET_HDR = 0x00004000
223 TUN_PKT_STRIP = 0x00000001
224 IFHWADDRLEN = 0x00000006
225 IFNAMSIZ = 0x00000010
226 IFREQ_SZ = 0x00000028
227 FIONREAD = 0x0000541b
229 class MulticastThread(threading.Thread):
230 def __init__(self, *p, **kw):
231 super(MulticastThread, self).__init__(*p, **kw)
232 self.igmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IGMP)
233 self.igmp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF,
234 socket.inet_aton(options.vif_addr) )
235 self.igmp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
236 self.igmp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
241 devnull = open('/dev/null','r+b')
242 maddr_re = re.compile(r"\s*inet\s*(\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3})\s*")
244 lastfullrefresh = time.time()
245 while not self._stop:
246 # Get current subscriptions
247 proc = subprocess.Popen(['ip','maddr','show',tun_name],
248 stdout = subprocess.PIPE,
249 stderr = subprocess.STDOUT,
252 for line in proc.stdout:
253 match = maddr_re.match(line)
255 new_maddr.add(match.group(1))
258 # Every now and then, send a full report
260 report_new = new_maddr
261 if (now - lastfullrefresh) <= 30.0:
262 report_new = report_new - cur_maddr
264 lastfullrefresh = now
266 # Report subscriptions
267 for grp in report_new:
268 igmpp = ipaddr2.ipigmp(
269 options.vif_addr, '224.0.0.2', 1, 0x16, 0, grp,
271 self.igmp_socket.sendto(igmpp, 0, ('224.0.0.2',0))
274 for grp in cur_maddr - new_maddr:
275 igmpp = ipaddr2.ipigmp(
276 options.vif_addr, '224.0.0.2', 1, 0x17, 0, grp,
278 self.igmp_socket.sendto(igmpp, 0, ('224.0.0.2',0))
280 cur_maddr = new_maddr
288 class HostLock(object):
289 # This class is used as a lock to prevent concurrency issues with more
290 # than one instance of netns running in the same machine. Both in
291 # different processes or different threads.
293 processcond = threading.Condition()
295 def __init__(self, lockfile):
296 processcond = self.__class__.processcond
298 processcond.acquire()
301 while self.__class__.taken:
303 self.__class__.taken = True
305 processcond.release()
307 self.lockfile = lockfile
311 fcntl.flock(self.lockfile, fcntl.LOCK_EX)
313 except (OSError, IOError), e:
314 if e.args[0] != os.errno.EINTR:
318 processcond = self.__class__.processcond
320 processcond.acquire()
322 if not self.lockfile.closed:
323 fcntl.flock(self.lockfile, fcntl.LOCK_UN)
326 self.__class__.taken = False
329 processcond.release()
332 return x+'\x00'*(IFNAMSIZ-len(x))
334 def ifreq(iface, flags):
336 # char[IFNAMSIZ] : interface name
339 ifreq = ifnam(iface)+struct.pack("H",flags);
340 ifreq += '\x00' * (len(ifreq)-IFREQ_SZ)
343 def tunopen(tun_path, tun_name):
344 if tun_path.isdigit():
346 print >>sys.stderr, "Using tun:", tun_name, "fd", tun_path
347 tun = os.fdopen(int(tun_path), 'r+b', 0)
350 print >>sys.stderr, "Using tun:", tun_name, "at", tun_path
351 tun = open(tun_path, 'r+b', 0)
353 # bind file descriptor to the interface
354 fcntl.ioctl(tun.fileno(), TUNSETIFF, ifreq(tun_name, IFF_NO_PI|IFF_TUN))
358 def tunclose(tun_path, tun_name, tun):
359 if tun_path and tun_path.isdigit():
361 os.close(int(tun_path))
367 def noopen(tun_path, tun_name):
368 print >>sys.stderr, "Using tun:", tun_name
370 def noclose(tun_path, tun_name, tun):
373 def tuntap_alloc(kind, tun_path, tun_name):
379 args.append(tun_name)
380 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
381 out,err = proc.communicate()
383 raise RuntimeError, "Could not allocate %s device" % (kind,)
385 match = re.search(r"Set '(?P<dev>(?:tun|tap)[0-9]*)' persistent and owned by .*", out, re.I)
387 raise RuntimeError, "Could not allocate %s device - tunctl said: %s" % (kind, out)
389 tun_name = match.group("dev")
390 print >>sys.stderr, "Allocated %s device: %s" % (kind, tun_name)
392 return tun_path, tun_name
394 def tuntap_dealloc(tun_path, tun_name):
395 args = ["tunctl", "-d", tun_name]
396 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
397 out,err = proc.communicate()
399 print >> sys.stderr, "WARNING: error deallocating %s device" % (tun_name,)
401 def nmask_to_dot_notation(mask):
402 mask = hex(((1 << mask) - 1) << (32 - mask)) # 24 -> 0xFFFFFF00
403 mask = mask[2:] # strip 0x
404 mask = mask.decode("hex") # to bytes
405 mask = '.'.join(map(str,map(ord,mask))) # to 255.255.255.0
408 def vif_start(tun_path, tun_name):
409 args = ["ifconfig", tun_name, options.vif_addr,
410 "netmask", nmask_to_dot_notation(options.vif_mask),
412 if options.vif_pointopoint:
413 args.extend(["pointopoint",options.vif_pointopoint])
414 if options.vif_txqueuelen is not None:
415 args.extend(["txqueuelen",str(options.vif_txqueuelen)])
417 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
418 out,err = proc.communicate()
420 raise RuntimeError, "Error starting virtual interface"
423 # set up SNAT using iptables
424 # TODO: stop vif on error.
425 # Not so necessary since deallocating the tun/tap device
426 # will forcibly stop it, but it would be tidier
427 args = [ "iptables", "-t", "nat", "-A", "POSTROUTING",
428 "-s", "%s/%d" % (options.vif_addr, options.vif_mask),
430 "--to-source", hostaddr, "--random" ]
431 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
432 out,err = proc.communicate()
434 raise RuntimeError, "Error setting up SNAT"
436 def vif_stop(tun_path, tun_name):
438 # set up SNAT using iptables
439 args = [ "iptables", "-t", "nat", "-D", "POSTROUTING",
440 "-s", "%s/%d" % (options.vif_addr, options.vif_mask),
442 "--to-source", hostaddr, "--random" ]
443 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
444 out,err = proc.communicate()
446 args = ["ifconfig", tun_name, "down"]
447 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
448 out,err = proc.communicate()
450 print >>sys.stderr, "WARNING: error stopping virtual interface"
453 def pl_tuntap_alloc(kind, tun_path, tun_name):
454 tunalloc_so = ctypes.cdll.LoadLibrary("./tunalloc.so")
455 c_tun_name = ctypes.c_char_p("\x00"*IFNAMSIZ) # the string will be mutated!
456 kind = {"tun":IFF_TUN,
458 fd = tunalloc_so.tun_alloc(kind, c_tun_name)
459 name = c_tun_name.value
462 _name_reservation = None
463 def pl_tuntap_namealloc(kind, tun_path, tun_name):
464 global _name_reservation
466 lockfile = open("/tmp/nepi-tun-connect.lock", "a")
467 lock = HostLock(lockfile)
469 # We need to do this, fd_tuntap is the only one who can
470 # tell us our slice id (this script runs as root, so no uid),
471 # and the pattern of device names accepted by vsys scripts
472 tunalloc_so = ctypes.cdll.LoadLibrary("./tunalloc.so")
473 c_tun_name = ctypes.c_char_p("\x00"*IFNAMSIZ) # the string will be mutated!
474 nkind= {"tun":IFF_TUN,
476 fd = tunalloc_so.tun_alloc(nkind, c_tun_name)
477 name = c_tun_name.value
480 base = name[:name.index('-')+1]
481 existing = set(map(str.strip,os.popen("ip a | grep -o '%s[0-9]*'" % (base,)).read().strip().split('\n')))
483 for i in xrange(9000,10000):
485 if name not in existing:
488 raise RuntimeError, "Could not assign interface name"
490 _name_reservation = lock
494 def pl_vif_start(tun_path, tun_name):
495 global _name_reservation
499 out.append(stdout.read())
503 # Serialize access to vsys
504 lockfile = open("/tmp/nepi-tun-connect.lock", "a")
505 lock = _name_reservation or HostLock(lockfile)
506 _name_reservation = None
508 stdin = open("/vsys/vif_up.in","w")
509 stdout = open("/vsys/vif_up.out","r")
511 t = threading.Thread(target=outreader)
514 stdin.write(tun_name+"\n")
515 stdin.write(options.vif_addr+"\n")
516 stdin.write(str(options.vif_mask)+"\n")
518 stdin.write("snat=1\n")
519 if options.vif_pointopoint:
520 stdin.write("pointopoint=%s\n" % (options.vif_pointopoint,))
521 if options.vif_txqueuelen is not None:
522 stdin.write("txqueuelen=%d\n" % (options.vif_txqueuelen,))
523 if options.mode.startswith('pl-gre'):
524 stdin.write("gre=%s\n" % (options.gre_key,))
525 stdin.write("remote=%s\n" % (remaining_args[0],))
531 print >>sys.stderr, out
535 def pl_vif_stop(tun_path, tun_name):
538 out.append(stdout.read())
541 if options.mode.startswith('pl-gre'):
546 for i in xrange(lim):
547 ifaces = set(map(str.strip,os.popen("ip a | grep -o '%s'" % (tun_name,)).read().strip().split('\n')))
548 if tun_name in ifaces:
553 # Serialize access to vsys
554 lockfile = open("/tmp/nepi-tun-connect.lock", "a")
555 lock = HostLock(lockfile)
557 stdin = open("/vsys/vif_down.in","w")
558 stdout = open("/vsys/vif_down.out","r")
560 t = threading.Thread(target=outreader)
563 stdin.write(tun_name+"\n")
569 print >>sys.stderr, out
574 def tun_fwd(tun, remote, reconnect = None, accept_local = None, accept_remote = None, slowlocal = True, bwlimit = None):
577 tunqueue = options.vif_txqueuelen or 1000
580 # in PL mode, we cannot strip PI structs
581 # so we'll have to handle them
582 tunchannel.tun_fwd(tun, remote,
583 with_pi = options.mode.startswith('pl-'),
584 ether_mode = tun_name.startswith('tap'),
585 cipher_key = options.cipher_key,
587 TERMINATE = TERMINATE,
589 reconnect = reconnect,
591 tunkqueue = tunkqueue,
592 cipher = options.cipher,
593 accept_local = accept_local,
594 accept_remote = accept_remote,
595 queueclass = queueclass,
596 slowlocal = slowlocal,
602 nop = lambda tun_path, tun_name : (tun_path, tun_name)
604 'none' : dict(alloc=nop,
605 tunopen=tunopen, tunclose=tunclose,
609 'tun' : dict(alloc=functools.partial(tuntap_alloc, "tun"),
610 tunopen=tunopen, tunclose=tunclose,
611 dealloc=tuntap_dealloc,
614 'tap' : dict(alloc=functools.partial(tuntap_alloc, "tap"),
615 tunopen=tunopen, tunclose=tunclose,
616 dealloc=tuntap_dealloc,
619 'pl-tun' : dict(alloc=functools.partial(pl_tuntap_alloc, "tun"),
620 tunopen=tunopen, tunclose=tunclose,
624 'pl-tap' : dict(alloc=functools.partial(pl_tuntap_alloc, "tap"),
625 tunopen=tunopen, tunclose=tunclose,
629 'pl-gre-ip' : dict(alloc=functools.partial(pl_tuntap_namealloc, "tun"),
630 tunopen=noopen, tunclose=tunclose,
634 'pl-gre-eth': dict(alloc=functools.partial(pl_tuntap_namealloc, "tap"),
635 tunopen=noopen, tunclose=noclose,
641 tun_path = options.tun_path
642 tun_name = options.tun_name
644 modeinfo = MODEINFO[options.mode]
646 # Try to load filter module
648 if options.filter_module:
649 print >>sys.stderr, "Loading module", options.filter_module, "with args", options.filter_args
650 if options.filter_module.endswith('.py'):
651 sys.path.append(os.path.dirname(options.filter_module))
652 filter_module = __import__(os.path.basename(options.filter_module).rsplit('.',1)[0])
653 if options.filter_args:
655 filter_args = dict(map(lambda x:x.split('=',1),options.filter_args.split(',')))
656 filter_module.init(**filter_args)
659 elif options.filter_module.endswith('.so'):
660 filter_module = ctypes.cdll.LoadLibrary(options.filter_module)
661 if options.filter_args:
663 filter_module.init(options.filter_args)
667 accept_packet = filter_module.accept_packet
668 print >>sys.stderr, "Installing packet filter (accept_packet)"
673 queueclass = filter_module.queueclass
674 print >>sys.stderr, "Installing custom queue"
679 _filter_init = filter_module.filter_init
680 filter_run = filter_module.filter_run
681 filter_close = filter_module.filter_close
684 filter_local = ctypes.c_int(0)
685 filter_remote = ctypes.c_int(0)
686 _filter_init(filter_local, filter_remote)
687 return filter_local, filter_remote
689 print >>sys.stderr, "Installing packet filter (stream filter)"
701 # install multicast forwarding hook
702 if options.multicast_fwd:
703 print >>sys.stderr, "Connecting to mcast filter"
704 mcfwd_sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
705 tunchannel.nonblock(mcfwd_sock.fileno())
707 # be careful to roll back stuff on exceptions
708 tun_path, tun_name = modeinfo['alloc'](tun_path, tun_name)
710 modeinfo['start'](tun_path, tun_name)
712 tun = modeinfo['tunopen'](tun_path, tun_name)
714 modeinfo['stop'](tun_path, tun_name)
717 modeinfo['dealloc'](tun_path, tun_name)
721 # Trak SIGTERM, and set global termination flag instead of dying
723 def _finalize(sig,frame):
725 TERMINATE.append(None)
726 signal.signal(signal.SIGTERM, _finalize)
733 # install multicast forwarding hook
734 if options.multicast_fwd:
735 print >>sys.stderr, "Installing mcast filter"
738 writev = iovec.writev
743 def writev(fileno, *stuff):
744 os_write(''.join(map_(str_,stuff)))
746 def accept_packet(packet, direction,
747 _up_accept=accept_packet,
749 sockno=mcfwd_sock.fileno(),
750 etherProto=tunchannel.etherProto,
751 etherStrip=tunchannel.etherStrip,
752 etherMode=tun_name.startswith('tap'),
753 multicast_fwd = options.multicast_fwd,
754 vif_addr = socket.inet_aton(options.vif_addr),
755 connected = [], writev=writev,
758 rv = _up_accept(packet, direction)
765 if etherProto(packet)=='\x08\x00':
766 fwd = etherStrip(packet)
771 if fwd is not None and len(fwd) >= 20:
772 if (ord(fwd[16]) & 0xf0) == 0xe0:
776 sock.connect(multicast_fwd)
777 connected.append(None)
779 traceback.print_exc(file=sys.stderr)
782 writev(sockno, vif_addr,fwd)
784 traceback.print_exc(file=sys.stderr)
789 if accept_packet or filter_init:
790 raise NotImplementedError, "--pass-fd and --filter are not compatible"
792 if options.pass_fd.startswith("base64:"):
793 options.pass_fd = base64.b64decode(
794 options.pass_fd[len("base64:"):])
795 options.pass_fd = os.path.expandvars(options.pass_fd)
797 print >>sys.stderr, "Sending FD to: %r" % (options.pass_fd,)
799 # send FD to whoever wants it
802 sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
806 raise OSError, "Killed"
808 sock.connect(options.pass_fd)
811 # wait a while, retry
812 print >>sys.stderr, "%s: Could not connect. Retrying in a sec..." % (time.strftime('%c'),)
813 time.sleep(min(30.0,retrydelay))
816 sock.connect(options.pass_fd)
817 passfd.sendfd(sock, tun.fileno(), '0')
820 def tun_fwd(tun, remote, **kw):
826 elif options.mode.startswith('pl-gre'):
827 if accept_packet or filter_init:
828 raise NotImplementedError, "--mode %s and --filter are not compatible" % (options.mode,)
831 def tun_fwd(tun, remote, **kw):
836 remote = remaining_args[0]
838 # connect to remote endpoint
839 if remaining_args and not remaining_args[0].startswith('-'):
840 print >>sys.stderr, "Listening at: %s:%d" % (hostaddr,options.udp)
841 print >>sys.stderr, "Connecting to: %s:%d" % (remaining_args[0],options.port)
842 rsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
846 raise OSError, "Killed"
848 rsock.bind((hostaddr,options.udp))
851 # wait a while, retry
852 print >>sys.stderr, "%s: Could not bind. Retrying in a sec..." % (time.strftime('%c'),)
853 time.sleep(min(30.0,retrydelay))
856 rsock.bind((hostaddr,options.udp))
857 rsock.connect((remaining_args[0],options.port))
859 print >>sys.stderr, "Error: need a remote endpoint in UDP mode"
860 raise AssertionError, "Error: need a remote endpoint in UDP mode"
862 # Wait for other peer
863 tunchannel.udp_handshake(TERMINATE, rsock)
865 remote = os.fdopen(rsock.fileno(), 'r+b', 0)
867 # connect to remote endpoint
868 if remaining_args and not remaining_args[0].startswith('-'):
869 print >>sys.stderr, "Connecting to: %s:%d" % (remaining_args[0],options.port)
870 rsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
874 raise OSError, "Killed"
876 rsock.connect((remaining_args[0],options.port))
879 # wait a while, retry
880 print >>sys.stderr, "%s: Could not connect. Retrying in a sec..." % (time.strftime('%c'),)
881 time.sleep(min(30.0,retrydelay))
884 rsock.connect((remaining_args[0],options.port))
886 print >>sys.stderr, "Listening at: %s:%d" % (hostaddr,options.port)
887 lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
891 raise OSError, "Killed"
893 lsock.bind((hostaddr,options.port))
896 # wait a while, retry
897 print >>sys.stderr, "%s: Could not bind. Retrying in a sec..." % (time.strftime('%c'),)
898 time.sleep(min(30.0,retrydelay))
901 lsock.bind((hostaddr,options.port))
903 rsock,raddr = lsock.accept()
904 remote = os.fdopen(rsock.fileno(), 'r+b', 0)
907 filter_local, filter_remote = filter_init()
914 remote = filter_remote
917 filter_close(local, remote)
919 filter_thread = threading.Thread(target=filter_loop)
920 filter_thread.start()
922 print >>sys.stderr, "Connected"
924 if not options.no_capture:
925 # Launch a tcpdump subprocess, to capture and dump packets.
926 # Make sure to catch sigterm and kill the tcpdump as well
927 tcpdump = subprocess.Popen(
928 ["tcpdump","-l","-n","-i",tun_name, "-s", "4096"]
929 + ["-w",options.pcap_capture,"-U"] * bool(options.pcap_capture) )
931 # Try to give us high priority
935 # Ignore errors, we might not have enough privileges,
936 # or perhaps there is no os.nice support in the system
939 if options.multicast:
940 # Start multicast forwarding daemon
941 mcastthread = MulticastThread()
946 reconnect = reconnect,
947 accept_local = accept_packet,
948 accept_remote = accept_packet,
949 bwlimit = options.bwlimit,
954 # 1. Forward packets from tun to filter
955 # 2. Forward packets from remote to filter
957 # 1. needs TUN rate-limiting, while
958 # 2. needs reconnection
960 # 1. needs ONLY TUN-side acceptance checks, while
961 # 2. needs ONLY remote-side acceptance checks
962 if isinstance(filter_local, ctypes.c_int):
963 filter_local_fd = filter_local.value
965 filter_local_fd = filter_local
966 if isinstance(filter_remote, ctypes.c_int):
967 filter_remote_fd = filter_remote.value
969 filter_remote_fd = filter_remote
972 tun_fwd(tun, filter_local_fd,
973 accept_local = accept_packet,
977 tun_fwd(filter_remote_fd, remote,
978 reconnect = reconnect,
979 accept_remote = accept_packet,
980 bwlimit = options.bwlimit,
983 localthread = threading.Thread(target=localside)
984 remotethread = threading.Thread(target=remoteside)
992 print >>sys.stderr, "Shutting down..."
994 # In case sys.stderr is broken
997 # tidy shutdown in every case - swallow exceptions
998 TERMINATE.append(None)
1008 filter_thread.join()
1014 os.kill(tcpdump.pid, signal.SIGTERM)
1020 modeinfo['stop'](tun_path, tun_name)
1022 traceback.print_exc()
1025 modeinfo['tunclose'](tun_path, tun_name, tun)
1027 traceback.print_exc()
1030 modeinfo['dealloc'](tun_path, tun_name)
1032 traceback.print_exc()
1034 print >>sys.stderr, "TERMINATED GRACEFULLY"