Total Pageviews

Tuesday 6 March 2012

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:
view plaincopy to clipboardprint?

    #!/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   = 0×0001
    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()

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

No comments:

Post a Comment