原文:http://xiaoxia.org/2012/02/21/udpip-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
([email protected]) 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()
--
Posted By GFW BLOG 功夫网与翻墙 to GFW BLOG(功夫网与翻墙) at 2/22/2012
09:22:00 AM
--
1、翻墙利器赛风3下载地址: http://dld.bz/caonima326 ,http://dld.bz/caonima745/
2、我们的订阅地址:http://feeds2.feedburner.com/chinagfwblog
3、停止订阅,请发邮件到
[email protected]
翻越防火长城,你可以到达世界上的每一个角落。(Across the Great Firewall, you can reach every corner in
the world.)