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试试
看起来挺不错,没有机会实验。