udpip: 用UDP封装IP数据包建立VPN

2012年2月23日 | 分类: 翻墙相关 | 标签: , , , ,

原理

使用Linux内核提供的tun设备建立可以在脚本读写的虚拟网卡,然后通过UDP将两个网卡的数据连接。
此方法能够使用以下特殊环境下:

1、客户端所在网络的路由不支持ppp,或者网络受到限制
2、TCP数据包被劫持或者受到限制
3、服务器是OpenVZ等不支持建立pptp,像我的burst的VPS就是这样子。

使用

服务器:

# python udptun.py -s 86 -l 10.0.0.1/24
Configuring interface t0 with ip 10.0.0.1/24

客户端:

# python udptun.py -c xiaoxia.org,86 -l 10.0.0.2/24
Configuring interface t0 with ip 10.0.0.2/24
Setting up new gateway …
Do login …
Logged in server succefully!

脚本代码

udptun.py:

  1. #!/usr/bin/python
  2. ””’
  3.     UDP Tunnel VPN
  4.     Xiaoxia (xiaoxia@xiaoxia.org)
  5.     Updated: 2012-2-21
  6. ”’
  7. import os, sys
  8. import hashlib
  9. import getopt
  10. import fcntl
  11. import time
  12. import struct
  13. import socket, select
  14. import traceback
  15. import signal
  16. import ctypes
  17. import binascii
  18. SHARED_PASSWORD = hashlib.sha1(“xiaoxia”).digest()
  19. TUNSETIFF = 0x400454ca
  20. IFF_TUN   = 0x0001
  21. BUFFER_SIZE = 8192
  22. MODE = 0
  23. DEBUG = 0
  24. PORT = 0
  25. IFACE_IP = “10.0.0.1/24”
  26. MTU = 1500
  27. TIMEOUT = 60*10 # seconds
  28. class Tunnel():
  29.     def create(self):
  30.         try:
  31.             self.tfd = os.open(“/dev/net/tun”, os.O_RDWR)
  32.         except:
  33.             self.tfd = os.open(“/dev/tun”, os.O_RDWR)
  34.         ifs = fcntl.ioctl(self.tfd, TUNSETIFF, struct.pack(“16sH”, “t%d”, IFF_TUN))
  35.         self.tname = ifs[:16].strip(“\x00”)
  36.     def close(self):
  37.         os.close(self.tfd)
  38.     def config(self, ip):
  39.         print “Configuring interface %s with ip %s” % (self.tname, ip)
  40.         os.system(“ip link set %s up” % (self.tname))
  41.         os.system(“ip link set %s mtu 1000” % (self.tname))
  42.         os.system(“ip addr add %s dev %s” % (ip, self.tname))
  43.     def config_routes(self):
  44.         if MODE == 1: # Server
  45.             pass
  46.         else: # Client
  47.             print “Setting up new gateway …”
  48.             # Look for default route
  49.             routes = os.popen(“ip route show”).readlines()
  50.             defaults = [x.rstrip() for x in routes if x.startswith(“default”)]
  51.             if not defaults:
  52.                 raise Exception(“Default route not found, maybe not connected!”)
  53.             self.prev_gateway = defaults[0]
  54.             self.prev_gateway_metric = self.prev_gateway + ” metric 2″
  55.             self.new_gateway = “default dev %s metric 1” % (self.tname)
  56.             self.tun_gateway = self.prev_gateway.replace(“default”, IP)
  57.             self.old_dns = file(“/etc/resolv.conf”, “rb”).read()
  58.             # Remove default gateway
  59.             os.system(“ip route del ” + self.prev_gateway)
  60.             # Add default gateway with metric
  61.             os.system(“ip route add ” + self.prev_gateway_metric)
  62.             # Add exception for server
  63.             os.system(“ip route add ” + self.tun_gateway)
  64.             # Add new default gateway
  65.             os.system(“ip route add ” + self.new_gateway)
  66.             # Set new DNS to 8.8.8.8
  67.             file(“/etc/resolv.conf”, “wb”).write(“nameserver 8.8.8.8”)
  68.     def restore_routes(self):
  69.         if MODE == 1: # Server
  70.             pass
  71.         else: # Client
  72.             print “Restoring previous gateway …”
  73.             os.system(“ip route del ” + self.new_gateway)
  74.             os.system(“ip route del ” + self.prev_gateway_metric)
  75.             os.system(“ip route del ” + self.tun_gateway)
  76.             os.system(“ip route add ” + self.prev_gateway)
  77.             file(“/etc/resolv.conf”, “wb”).write(self.old_dns)
  78.     def run(self):
  79.         global PORT
  80.         self.udpfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  81.         if MODE == 1:
  82.             self.udpfd.bind((“”, PORT))
  83.         else:
  84.             self.udpfd.bind((“”, 0))
  85.         self.clients = {}
  86.         self.logged = False
  87.         self.try_logins = 5
  88.         self.log_time = 0
  89.         while True:
  90.             if MODE == 2 and not self.logged and time.time() – self.log_time > 2.:
  91.                 print “Do login …”
  92.                 self.udpfd.sendto(“LOGIN:” + SHARED_PASSWORD + “:” +
  93.                     IFACE_IP.split(“/”)[0], (IP, PORT))
  94.                 self.try_logins -= 1
  95.                 if self.try_logins == 0:
  96.                     raise Exception(“Failed to log in server.”)
  97.                 self.log_time = time.time()
  98.             rset = select.select([self.udpfd, self.tfd], [], [], 1)[0]
  99.             for r in rset:
  100.                 if r == self.tfd:
  101.                     if DEBUG: os.write(1, “>”)
  102.                     data = os.read(self.tfd, MTU)
  103.                     if MODE == 1: # Server
  104.                         src, dst = data[16:20], data[20:24]
  105.                         for key in self.clients:
  106.                             if dst == self.clients[key][“localIPn”]:
  107.                                 self.udpfd.sendto(data, key)
  108.                         # Remove timeout clients
  109.                         curTime = time.time()
  110.                         for key in self.clients.keys():
  111.                             if curTime – self.clients[key][“aliveTime”] > TIMEOUT:
  112.                                 print “Remove timeout client”, key
  113.                                 del self.clients[key]
  114.                     else: # Client
  115.                         self.udpfd.sendto(data, (IP, PORT))
  116.                 elif r == self.udpfd:
  117.                     if DEBUG: os.write(1, “<“)
  118.                     data, src = self.udpfd.recvfrom(BUFFER_SIZE)
  119.                     if MODE == 1: # Server
  120.                         key = src
  121.                         if key not in self.clients:
  122.                             # New client comes
  123.                             try:
  124.                                 if data.startswith(“LOGIN:”) and data.split(“:”)[1]==SHARED_PASSWORD:
  125.                                     localIP = data.split(“:”)[2]
  126.                                     self.clients[key] = {“aliveTime”: time.time(),
  127.                                                         “localIPn”: socket.inet_aton(localIP)}
  128.                                     print “New Client from”, src, “request IP”, localIP
  129.                                     self.udpfd.sendto(“LOGIN:SUCCESS”, src)
  130.                             except:
  131.                                 print “Need valid password from”, src
  132.                                 self.udpfd.sendto(“LOGIN:PASSWORD”, src)
  133.                         else:
  134.                             # Simply write the packet to local or forward them to other clients ???
  135.                             os.write(self.tfd, data)
  136.                             self.clients[key][“aliveTime”] = time.time()
  137.                     else: # Client
  138.                         if data.startswith(“LOGIN”):
  139.                             if data.endswith(“PASSWORD”):
  140.                                 self.logged = False
  141.                                 print “Need password to login!”
  142.                             elif data.endswith(“SUCCESS”):
  143.                                 self.logged = True
  144.                                 self.try_logins = 5
  145.                                 print “Logged in server succefully!”
  146.                         else:
  147.                             os.write(self.tfd, data)
  148. def usage(status = 0):
  149.     print “Usage: %s [-s port|-c serverip] [-hd] [-l localip]” % (sys.argv[0])
  150.     sys.exit(status)
  151. def on_exit(no, info):
  152.     raise Exception(“TERM signal caught!”)
  153. if __name__==”__main__”:
  154.     opts = getopt.getopt(sys.argv[1:],”s:c:l:hd”)
  155.     for opt,optarg in opts[0]:
  156.         if opt == “-h”:
  157.             usage()
  158.         elif opt == “-d”:
  159.             DEBUG += 1
  160.         elif opt == “-s”:
  161.             MODE = 1
  162.             PORT = int(optarg)
  163.         elif opt == “-c”:
  164.             MODE = 2
  165.             IP, PORT = optarg.split(“,”)
  166.             IP = socket.gethostbyname(IP)
  167.             PORT = int(PORT)
  168.         elif opt == “-l”:
  169.             IFACE_IP = optarg
  170.     if MODE == 0 or PORT == 0:
  171.         usage(1)
  172.     tun = Tunnel()
  173.     tun.create()
  174.     tun.config(IFACE_IP)
  175.     signal.signal(signal.SIGTERM, on_exit)
  176.     tun.config_routes()
  177.     try:
  178.         tun.run()
  179.     except KeyboardInterrupt:
  180.         pass
  181.     except:
  182.         print traceback.format_exc()
  183.     finally:
  184.         tun.restore_routes()
  185.         tun.close()

来源http://xiaoxia.org/2012/02/21/udpip-vpn/

  1. dd
    2012年7月4日11:27

    hi.
    hi.怎么我把这段代码保存为udtun.py文件后,在本地windows机器下,双击该py文件,弹出一个dos窗口,闪了一下就消失了?我的windows机器装了python-2.7.3,博主有用过他这个东西成功翻墙吗

    • iGFW
      2012年7月4日11:30

      这个需要你有VPS或独立服务器运行服务器端,然后在linux系统上才能使用这个VPN。

      • dd
        2012年7月13日12:24

        hi.我在ubuntu下,按此文设置成功,不过翻墙失败。博主曾经按此文的设置成功的翻墙了吗?另外博主按此文-
        http://igfw.net/archives/7613,也成功的翻过墙吗

        • iGFW
          2012年7月13日14:28

          这两篇文中方法我都没有测试过,如果你遇到问题,可以点击查看原文,问问原文作者,或可解决。

  2. wave
    2012年2月24日09:39

    呵呵 有些复杂 博主要是能修改一下大众都能易懂就更好了

    • iGFW
      2012年2月24日10:56

      呵呵,这个我也不懂

  3. ym
    2012年2月23日20:58

    hi.博主用vpn能访问youtube吗?我这里显示connection reset,其他被封网站均可访问。真是活见鬼了

    • GayShyFool
      2012年2月23日22:35

      ym :hi.博主用vpn能访问youtube吗?我这里显示connection reset,其他被封网站均可访问。真是活见鬼了

      可能你用的vpn限制youtube,换一个试试

    • iGFW
      2012年2月24日08:32

      能,你修改本机DNS为国外,然后刷新本机DNS缓存和浏览器缓存再试试。

  4. 天生多疑
    2012年2月23日20:12

    希望TA能搞个自己的协议出来,兼容其他所有协议,然后我们就畅通无阻的上网,呵呵.

  5. 天生多疑
    2012年2月23日20:11

    xiaoxia这家伙,研究网络协议很深入。

  6. GayShyFool
    2012年2月23日17:59

    貌似好东西呀,电信路由器限制就是只拦截tcp,要是用这个的话估计就无敌了~……就是没有vps试试

  7. 小zz的好基友
    2012年2月23日09:51

    看起来挺不错,没有机会实验。