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   = 0×0001
  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. 小zz的好基友
    2012年2月23日09:51

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

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

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

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

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

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

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

  5. 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缓存和浏览器缓存再试试。

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

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

    • iGFW
      2012年2月24日10:56

      呵呵,这个我也不懂