udpip: 用UDP封装IP数据包建立VPN
原理
使用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:
- #!/usr/bin/python
 - ””’
 - UDP Tunnel VPN
 - Xiaoxia (xiaoxia@xiaoxia.org)
 - Updated: 2012-2-21
 - ”’
 - import os, sys
 - import hashlib
 - import getopt
 - import fcntl
 - import time
 - import struct
 - import socket, select
 - import traceback
 - import signal
 - import ctypes
 - import binascii
 - SHARED_PASSWORD = hashlib.sha1(“xiaoxia”).digest()
 - TUNSETIFF = 0x400454ca
 - IFF_TUN = 0x0001
 - BUFFER_SIZE = 8192
 - MODE = 0
 - DEBUG = 0
 - PORT = 0
 - IFACE_IP = “10.0.0.1/24”
 - MTU = 1500
 - TIMEOUT = 60*10 # seconds
 - class Tunnel():
 - def create(self):
 - try:
 - self.tfd = os.open(“/dev/net/tun”, os.O_RDWR)
 - except:
 - self.tfd = os.open(“/dev/tun”, os.O_RDWR)
 - ifs = fcntl.ioctl(self.tfd, TUNSETIFF, struct.pack(“16sH”, “t%d”, IFF_TUN))
 - self.tname = ifs[:16].strip(“\x00”)
 - def close(self):
 - os.close(self.tfd)
 - def config(self, ip):
 - print “Configuring interface %s with ip %s” % (self.tname, ip)
 - os.system(“ip link set %s up” % (self.tname))
 - os.system(“ip link set %s mtu 1000” % (self.tname))
 - os.system(“ip addr add %s dev %s” % (ip, self.tname))
 - def config_routes(self):
 - if MODE == 1: # Server
 - pass
 - else: # Client
 - print “Setting up new gateway …”
 - # Look for default route
 - routes = os.popen(“ip route show”).readlines()
 - defaults = [x.rstrip() for x in routes if x.startswith(“default”)]
 - if not defaults:
 - raise Exception(“Default route not found, maybe not connected!”)
 - self.prev_gateway = defaults[0]
 - self.prev_gateway_metric = self.prev_gateway + ” metric 2″
 - self.new_gateway = “default dev %s metric 1” % (self.tname)
 - self.tun_gateway = self.prev_gateway.replace(“default”, IP)
 - self.old_dns = file(“/etc/resolv.conf”, “rb”).read()
 - # Remove default gateway
 - os.system(“ip route del ” + self.prev_gateway)
 - # Add default gateway with metric
 - os.system(“ip route add ” + self.prev_gateway_metric)
 - # Add exception for server
 - os.system(“ip route add ” + self.tun_gateway)
 - # Add new default gateway
 - os.system(“ip route add ” + self.new_gateway)
 - # Set new DNS to 8.8.8.8
 - file(“/etc/resolv.conf”, “wb”).write(“nameserver 8.8.8.8”)
 - def restore_routes(self):
 - if MODE == 1: # Server
 - pass
 - else: # Client
 - print “Restoring previous gateway …”
 - os.system(“ip route del ” + self.new_gateway)
 - os.system(“ip route del ” + self.prev_gateway_metric)
 - os.system(“ip route del ” + self.tun_gateway)
 - os.system(“ip route add ” + self.prev_gateway)
 - file(“/etc/resolv.conf”, “wb”).write(self.old_dns)
 - def run(self):
 - global PORT
 - self.udpfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 - if MODE == 1:
 - self.udpfd.bind((“”, PORT))
 - else:
 - self.udpfd.bind((“”, 0))
 - self.clients = {}
 - self.logged = False
 - self.try_logins = 5
 - self.log_time = 0
 - while True:
 - if MODE == 2 and not self.logged and time.time() – self.log_time > 2.:
 - print “Do login …”
 - self.udpfd.sendto(“LOGIN:” + SHARED_PASSWORD + “:” +
 - IFACE_IP.split(“/”)[0], (IP, PORT))
 - self.try_logins -= 1
 - if self.try_logins == 0:
 - raise Exception(“Failed to log in server.”)
 - self.log_time = time.time()
 - rset = select.select([self.udpfd, self.tfd], [], [], 1)[0]
 - for r in rset:
 - if r == self.tfd:
 - if DEBUG: os.write(1, “>”)
 - data = os.read(self.tfd, MTU)
 - if MODE == 1: # Server
 - src, dst = data[16:20], data[20:24]
 - for key in self.clients:
 - if dst == self.clients[key][“localIPn”]:
 - self.udpfd.sendto(data, key)
 - # Remove timeout clients
 - curTime = time.time()
 - for key in self.clients.keys():
 - if curTime – self.clients[key][“aliveTime”] > TIMEOUT:
 - print “Remove timeout client”, key
 - del self.clients[key]
 - else: # Client
 - self.udpfd.sendto(data, (IP, PORT))
 - elif r == self.udpfd:
 - if DEBUG: os.write(1, “<“)
 - data, src = self.udpfd.recvfrom(BUFFER_SIZE)
 - if MODE == 1: # Server
 - key = src
 - if key not in self.clients:
 - # New client comes
 - try:
 - if data.startswith(“LOGIN:”) and data.split(“:”)[1]==SHARED_PASSWORD:
 - localIP = data.split(“:”)[2]
 - self.clients[key] = {“aliveTime”: time.time(),
 - “localIPn”: socket.inet_aton(localIP)}
 - print “New Client from”, src, “request IP”, localIP
 - self.udpfd.sendto(“LOGIN:SUCCESS”, src)
 - except:
 - print “Need valid password from”, src
 - self.udpfd.sendto(“LOGIN:PASSWORD”, src)
 - else:
 - # Simply write the packet to local or forward them to other clients ???
 - os.write(self.tfd, data)
 - self.clients[key][“aliveTime”] = time.time()
 - else: # Client
 - if data.startswith(“LOGIN”):
 - if data.endswith(“PASSWORD”):
 - self.logged = False
 - print “Need password to login!”
 - elif data.endswith(“SUCCESS”):
 - self.logged = True
 - self.try_logins = 5
 - print “Logged in server succefully!”
 - else:
 - os.write(self.tfd, data)
 - def usage(status = 0):
 - print “Usage: %s [-s port|-c serverip] [-hd] [-l localip]” % (sys.argv[0])
 - sys.exit(status)
 - def on_exit(no, info):
 - raise Exception(“TERM signal caught!”)
 - if __name__==”__main__”:
 - opts = getopt.getopt(sys.argv[1:],”s:c:l:hd”)
 - for opt,optarg in opts[0]:
 - if opt == “-h”:
 - usage()
 - elif opt == “-d”:
 - DEBUG += 1
 - elif opt == “-s”:
 - MODE = 1
 - PORT = int(optarg)
 - elif opt == “-c”:
 - MODE = 2
 - IP, PORT = optarg.split(“,”)
 - IP = socket.gethostbyname(IP)
 - PORT = int(PORT)
 - elif opt == “-l”:
 - IFACE_IP = optarg
 - if MODE == 0 or PORT == 0:
 - usage(1)
 - tun = Tunnel()
 - tun.create()
 - tun.config(IFACE_IP)
 - signal.signal(signal.SIGTERM, on_exit)
 - tun.config_routes()
 - try:
 - tun.run()
 - except KeyboardInterrupt:
 - pass
 - except:
 - print traceback.format_exc()
 - finally:
 - tun.restore_routes()
 - tun.close()
 

hi.
hi.怎么我把这段代码保存为udtun.py文件后,在本地windows机器下,双击该py文件,弹出一个dos窗口,闪了一下就消失了?我的windows机器装了python-2.7.3,博主有用过他这个东西成功翻墙吗
这个需要你有VPS或独立服务器运行服务器端,然后在linux系统上才能使用这个VPN。
hi.我在ubuntu下,按此文设置成功,不过翻墙失败。博主曾经按此文的设置成功的翻墙了吗?另外博主按此文-
http://igfw.net/archives/7613,也成功的翻过墙吗
这两篇文中方法我都没有测试过,如果你遇到问题,可以点击查看原文,问问原文作者,或可解决。
呵呵 有些复杂 博主要是能修改一下大众都能易懂就更好了
呵呵,这个我也不懂
hi.博主用vpn能访问youtube吗?我这里显示connection reset,其他被封网站均可访问。真是活见鬼了
可能你用的vpn限制youtube,换一个试试
能,你修改本机DNS为国外,然后刷新本机DNS缓存和浏览器缓存再试试。
希望TA能搞个自己的协议出来,兼容其他所有协议,然后我们就畅通无阻的上网,呵呵.
xiaoxia这家伙,研究网络协议很深入。
貌似好东西呀,电信路由器限制就是只拦截tcp,要是用这个的话估计就无敌了~……就是没有vps试试
看起来挺不错,没有机会实验。