├── .gitignore ├── .project ├── .pydevproject ├── .settings └── org.eclipse.core.resources.prefs ├── LICENSE ├── README.md ├── ddns ├── pyvpn.py ├── src ├── README.md ├── client.py ├── server.py ├── test_client_protocol.py ├── util.py └── webconsole.py ├── tun-ping-responder.py ├── tun-ping-win.py ├── tuntest.py ├── utils.py └── vpn_client.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | pyvpn 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /${PROJECT_DIR_NAME} 5 | 6 | python 2.7 7 | Default 8 | 9 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/client.py=utf-8 3 | encoding//src/server.py=utf-8 4 | encoding//src/test_client_protocol.py=utf-8 5 | encoding//src/util.py=utf-8 6 | encoding//src/webconsole.py=utf-8 7 | encoding/ddns=utf-8 8 | encoding/pyvpn.py=utf-8 9 | encoding/tuntest.py=utf-8 10 | encoding/utils.py=utf-8 11 | encoding/vpn_client.py=utf-8 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pyvpn 2 | ===== 3 | 4 | python vpn server & client. 5 | 6 | server: 7 | eth0: 192.168.0.192/24, listen on eth0 23456, communication with tcp 8 | tun0: 192.168.10.1/24 9 | 10 | 11 | client: 12 | eth0: 192.168.2.108/24 13 | tun0: 192.168.10.2/24 14 | ioctl a tun device; 15 | set 192.168.0.1/24 to this tun; 16 | connect to heruilong1988.oicp.net 23456, establish connection, large conn with heartbeat 17 | 18 | TUNSETIFF = 0x400454ca 19 | TUNSETOWNER = TUNSETIFF + 2 20 | IFF_TUN = 0x0001 21 | IFF_NO_PI = 0x1000 22 | 23 | # Open TUN device file. 24 | tun = open('/dev/net/tun', 'r+b') 25 | # Tall it we want a TUN device named tun0. 26 | ifr = struct.pack('16sH', 'tun%d', IFF_TUN | IFF_NO_PI) 27 | fcntl.ioctl(tun, TUNSETIFF, ifr) 28 | # Optionally, we want it be accessed by the normal user. 29 | fcntl.ioctl(tun, TUNSETOWNER, 1000) 30 | print ifr 31 | return tun.fileno() 32 | 33 | 34 | Usage: 35 | vpn -s 192.168.10.1 255.255.255.0 36 | 37 | vpn -c 192.168.10.2 255.255.255.0 -r office.server.org 23456 38 | 39 | 1111 1111 1111 1111 1111 1111 0000 0000 40 | 1100 0000 1010 1000 0000 1010 0000 0010 41 | 42 | 43 | -------------------------------------------------------------------------------- /ddns: -------------------------------------------------------------------------------- 1 | #!/usr/local/python2.7/bin/python 2 | # -*- coding:utf-8 -*- 3 | import sys 4 | import urllib2 5 | import urllib 6 | import json 7 | import time 8 | import socket 9 | import os 10 | import logging 11 | logger = logging.getLogger('ddns') 12 | logger.addHandler(logging.StreamHandler()) 13 | 14 | 15 | public_dic = {} 16 | public_dic["login_email"] = "" # replace your email 替换你的dnspod账号email 17 | public_dic["login_password"] = "" # replace your password 替换你的密码 18 | domain = "" # replace your domain 你的域名 19 | record = "" # replace your record 你的二级域名 20 | public_dic["format"] = "json" 21 | headers = {} 22 | headers["User-Agent"] = "lixinDDNS/1(lixin@lixin.me)" 23 | 24 | isCron = True # #是否作为定时任务执行,isCron==True 的话,则不会进入循环 25 | ip = '' 26 | sleepTime = 3000 27 | 28 | 29 | def saveIP(ip): 30 | f = open('./ddnsip.txt', 'w') 31 | f.write(ip) 32 | f.close() 33 | 34 | 35 | def readIP(): 36 | if not os.path.isfile('./ddnsip.txt'): 37 | return "" 38 | f = open('./ddnsip.txt', 'r') 39 | myip = f.read() 40 | f.close() 41 | return myip 42 | 43 | 44 | def WriteLog(msg): 45 | f = open('./ddns.log', 'a') 46 | f.write(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()) + " " + msg + "\n") 47 | f.close() 48 | 49 | 50 | def getDomainID(): 51 | url = "https://dnsapi.cn/Domain.Info" 52 | params = public_dic.copy() 53 | params["domain"] = domain 54 | req = urllib2.Request(url, headers=headers, data=urllib.urlencode(params)) 55 | resp = urllib2.urlopen(req) 56 | formatJson = json.load(resp) 57 | if formatJson["status"]["code"] != "1": 58 | WriteLog("getDomainID has Error: (" + formatJson["status"]["code"] + ")" + formatJson["status"]["message"]) 59 | return 0 60 | else: 61 | return formatJson["domain"]["id"] 62 | pass 63 | 64 | 65 | def getRecordID(domain_id): 66 | url = "https://dnsapi.cn/Record.List" 67 | params = public_dic.copy() 68 | params["domain_id"] = domain_id 69 | params["sub_domain"] = record 70 | req = urllib2.Request(url, headers=headers, data=urllib.urlencode(params)) 71 | resp = urllib2.urlopen(req) 72 | myJson = json.load(resp) 73 | ip = myJson["records"][0]['value'] 74 | return myJson["records"][0]['id'] 75 | 76 | 77 | def getMyIp(): 78 | url = "ns1.dnspod.net" 79 | port = 6666 80 | mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 81 | mySocket.connect((url, port)) 82 | recv = mySocket.recv(16) 83 | mySocket.close() 84 | return recv 85 | 86 | 87 | def setDDNS(domainID, recordID): 88 | url = "https://dnsapi.cn/Record.Ddns" 89 | params = public_dic.copy() 90 | params["domain_id"] = domainID 91 | params["sub_domain"] = record 92 | params["record_id"] = recordID 93 | params["record_line"] = "默认" 94 | req = urllib2.Request(url, headers=headers, data=urllib.urlencode(params)) 95 | resp = urllib2.urlopen(req) 96 | myJson = json.load(resp) 97 | if myJson["status"]["code"] != "1": 98 | WriteLog("setDDNS has Error: (" + myJson["status"]["code"] + ")" + myJson["status"]["message"]) 99 | 100 | 101 | def run(email=None, password=None, Domain=domain, Record=record): 102 | public_dic["login_email"] = email or public_dic["login_email"] 103 | public_dic["login_password"] = password or public_dic["login_password"] 104 | domain = Domain 105 | record = Record 106 | try: 107 | newIP = getMyIp() 108 | oldIP = readIP() 109 | if oldIP == newIP: 110 | return 111 | domainID = getDomainID() 112 | if domainID == 0: 113 | return 114 | recordID = getRecordID(domainID) 115 | if recordID == 0: 116 | return 117 | setDDNS(domainID, recordID) 118 | saveIP(newIP) 119 | WriteLog("new ip=" + newIP) 120 | except Exception, e: 121 | WriteLog("has a ERROR:" + e.strerror) 122 | 123 | 124 | if __name__ == '__main__': 125 | if len(sys.argv) == 5: 126 | public_dic["login_email"] = sys.argv[1] 127 | public_dic["login_password"] = sys.argv[2] 128 | domain = sys.argv[3] 129 | record = sys.argv[4] 130 | if isCron: 131 | run() 132 | exit() 133 | while True: 134 | try: 135 | newIP = getMyIp() 136 | if ip != newIP: 137 | domainID = getDomainID() 138 | if domainID != 0: 139 | recordID = getRecordID(domainID) 140 | if recordID != 0: 141 | setDDNS(domainID, recordID) 142 | ip = newIP 143 | WriteLog("new ip=" + ip) 144 | pass 145 | except Exception, e: 146 | WriteLog("has a ERROR:" + e.strerror) 147 | time.sleep(sleepTime) 148 | pass 149 | -------------------------------------------------------------------------------- /pyvpn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | ''' 4 | Created on 2014年12月6日 5 | 6 | @author: Sunday 7 | server: 8 | eth0: 192.168.0.192/24, listen on eth0 23456, communication with tcp 9 | tun0: 192.168.10.1/24 10 | 11 | #TODO: 12 | 2, Data gzip 13 | 3, user auth 14 | 4, hub to switch 15 | 5, select to epoll 16 | 6, test global route 17 | 7, traffic counting 18 | 19 | protocol: 20 | ---------------------------- 21 | | | 22 | ---------------------------- 23 | header, body 24 | 1 byte, 4 byte, var byte 25 | data, push-ip, push-route, require-auth, auth-res 26 | ''' 27 | import fcntl # @UnresolvedImport 28 | import socket 29 | import select 30 | import os 31 | import logging 32 | import struct 33 | import time 34 | import sys 35 | import argparse 36 | logger = logging.getLogger('vpn') 37 | logger.addHandler(logging.StreamHandler()) 38 | logger.setLevel(logging.DEBUG) 39 | PYVPN_VERSION = '0.1' 40 | 41 | # find const values 42 | # grep IFF_UP -rl /usr/include/ 43 | IFF_UP = 0x1 44 | IFF_RUNNING = 0x40 45 | IFNAMSIZ = 16 46 | SIOCSIFADDR = 0x8916 47 | SIOCSIFNETMASK = 0x891c 48 | SIOCGIFFLAGS = 0x8913 49 | SIOCSIFFLAGS = 0x8914 50 | SIOCADDRT = 0x890B 51 | 52 | RTF_UP = 0x0001 53 | RTF_GATEWAY = 0x0002 54 | 55 | AF_INET = socket.AF_INET 56 | 57 | 58 | def to_int(s): 59 | try: 60 | return int(s) 61 | except ValueError as _unused: 62 | return None 63 | 64 | 65 | class exp_none(object): 66 | def __init__(self, fn): 67 | self.fn = fn 68 | 69 | def __call__(self, *args, **kwargs): 70 | try: 71 | return self.fn(*args, **kwargs) 72 | except Exception as e: 73 | logger.warn(e) 74 | return None 75 | 76 | 77 | def make_tun(): 78 | TUNSETIFF = 0x400454ca 79 | TUNSETOWNER = TUNSETIFF + 2 80 | IFF_TUN = 0x0001 81 | IFF_NO_PI = 0x1000 82 | 83 | # Open TUN device file. 84 | tun = open('/dev/net/tun', 'r+b') 85 | # Tall it we want a TUN device named tun0. 86 | ifr = struct.pack('16sH', 'tun%d', IFF_TUN | IFF_NO_PI) 87 | ret = fcntl.ioctl(tun, TUNSETIFF, ifr) 88 | dev, _ = struct.unpack('16sH', ret) 89 | dev = dev.strip() 90 | # Optionally, we want it be accessed by the normal user. 91 | fcntl.ioctl(tun, TUNSETOWNER, 1000) 92 | return dev, tun 93 | 94 | 95 | @exp_none 96 | def ifconfig(dev, ipaddr, netmask): 97 | # http://stackoverflow.com/questions/6652384/how-to-set-the-ip-address-from-c-in-linux 98 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_IP) 99 | AF_INET = socket.AF_INET 100 | fd = sock.fileno() 101 | addrbuf = struct.pack('BBBB', *[int(el) for el in ipaddr.split('.')]) 102 | maskbuf = struct.pack('BBBB', *[int(el) for el in netmask.split('.')]) 103 | sockaddr_mt = '16sHH4s' 104 | flags_mt = '16sH' 105 | # ADDR 106 | siocsifaddr = struct.pack(sockaddr_mt, dev, AF_INET, 0, addrbuf) 107 | fcntl.ioctl(fd, SIOCSIFADDR, siocsifaddr) 108 | # MASK 109 | siocsifnetmask = struct.pack(sockaddr_mt, dev, AF_INET, 0, maskbuf) 110 | fcntl.ioctl(fd, SIOCSIFNETMASK, siocsifnetmask) 111 | # ifconfig tun0 up 112 | ifr2 = struct.pack(flags_mt, dev, 0) 113 | ifr_ret = fcntl.ioctl(fd, SIOCGIFFLAGS, ifr2) 114 | cur_flags = struct.unpack(flags_mt, ifr_ret)[1] 115 | flags = cur_flags | (IFF_UP | IFF_RUNNING) 116 | ifr_ret = struct.pack(flags_mt, dev, flags) 117 | ifr_ret = fcntl.ioctl(fd, SIOCSIFFLAGS, ifr_ret) 118 | return 0 119 | 120 | 121 | @exp_none 122 | def add_route(dest, mask, gw): 123 | # sudo strace route add -net 192.168.0.0/24 gw 192.168.10.1 124 | # ioctl(3, SIOCADDRT, ifr) 125 | # /usr/include/net/route.h 126 | pad = '\x00' * 8 127 | inet_aton = socket.inet_aton 128 | sockaddr_in_fmt = 'hH4s8s' 129 | rtentry_fmt = 'L16s16s16sH38s' 130 | dst = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(dest), pad) 131 | next_gw = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(gw), pad) 132 | netmask = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(mask), pad) 133 | rt_flags = RTF_UP | RTF_GATEWAY 134 | rtentry = struct.pack(rtentry_fmt, 135 | 0, dst, next_gw, netmask, rt_flags, '\x00' * 38) 136 | sock = socket.socket(AF_INET, socket.SOCK_DGRAM, 0) 137 | fcntl.ioctl(sock.fileno(), SIOCADDRT, rtentry) 138 | return 0 139 | 140 | 141 | def conn_to_vpn(addr, port): 142 | sock = socket.socket() 143 | addr = (addr, port) 144 | try: 145 | sock.connect(addr) 146 | except socket.error as e: 147 | print 'Connect to VPN:[%d],[%s]' % (e.errno, e.strerror) 148 | return None 149 | sock.setblocking(False) 150 | return sock 151 | 152 | 153 | def enable_tcp_forward(): 154 | logger.info(u'Set ip_forward=1') 155 | with open('/proc/sys/net/ipv4/ip_forward', 'wb+') as f1: 156 | f1.seek(0) 157 | f1.write('1') 158 | 159 | 160 | class Transport(object): 161 | def __init__(self, sock): 162 | self.sock = sock 163 | self.buf = '' 164 | 165 | def set_tunfd(self, tunfd): 166 | self.tunfd = tunfd 167 | 168 | def get_frame(self, buf): 169 | if len(buf) <= 20: 170 | return -1 171 | pack_len = struct.unpack('!H', buf[2:4])[0] 172 | logger.info('FRAME:[%d], BUF:[%d]' % (pack_len, len(buf))) 173 | if len(buf) < pack_len: 174 | return -1 175 | return pack_len 176 | 177 | def recv(self, buf): 178 | self.buf += buf 179 | while True: 180 | # 一次只能写入一个 IP包,帧。 181 | length = self.get_frame(self.buf) 182 | if length == -1: 183 | break 184 | frame = self.buf[:length] 185 | self.buf = self.buf[length:] 186 | os.write(self.tunfd, frame) 187 | logger.info('Write to TUN:[%d]' % len(frame)) 188 | 189 | 190 | def client_main(ip, netmask, host, port): 191 | buflen = 65536 192 | dev, tundev = make_tun() 193 | tunfd = tundev.fileno() 194 | logger.info(u'TUN dev OK, FD:[%d]' % tunfd) 195 | time.sleep(1) 196 | iret = ifconfig(dev, ip, netmask) 197 | if iret is None: 198 | logger.info(u'ip config %s error' % dev) 199 | return sys.exit(1) 200 | iret = add_route('192.168.0.0', '255.255.255.0', '192.168.10.1') 201 | if iret is None: 202 | logger.info(u'route config %s error' % dev) 203 | return sys.exit(1) 204 | time.sleep(1) 205 | 206 | sock = conn_to_vpn(host, int(port)) 207 | if sock is None: 208 | print u'SOCK dev Fail' 209 | sys.exit(-1) 210 | client = Transport(sock) 211 | client.set_tunfd(tunfd) 212 | sockfd = sock.fileno() 213 | logger.info(u'SOCK dev OK, FD:[%d]' % sockfd) 214 | 215 | fds = [tunfd, sockfd, ] 216 | while True: 217 | rs, _, _ = select.select(fds, [], []) 218 | for fd in rs: 219 | if fd == tunfd: 220 | rcv = os.read(tunfd, buflen) 221 | if len(rcv) == 0: 222 | logger.warn(u'TUN recv [0], Continue') 223 | continue 224 | sent_len = sock.send(rcv) 225 | logger.info('TUN recv, write to SOCK:[%r]' % sent_len) 226 | elif fd == sockfd: 227 | rcv = sock.recv(buflen) 228 | if len(rcv) == 0: 229 | logger.warn(u'SOCK recv [0], break') 230 | os.close(sockfd) 231 | break 232 | logger.info('SOCK recv [%d]' % len(rcv)) 233 | client.recv(rcv) 234 | 235 | 236 | def server_main(gwip, netmask, lip, lport): 237 | buflen = 65536 238 | dev, tundev = make_tun() 239 | print 'Allocated %s' % dev 240 | tunfd = tundev.fileno() 241 | logger.info(u'TUN dev OK') 242 | ifconfig(dev, gwip, netmask) 243 | enable_tcp_forward() 244 | 245 | sock = socket.socket() 246 | laddr = (lip, int(lport)) 247 | sock.bind(laddr) 248 | sock.listen(socket.SOMAXCONN) 249 | logger.info(u'Sock Listen OK') 250 | sock.setblocking(False) 251 | sockfd = sock.fileno() 252 | clients = {} 253 | 254 | fds = [tunfd, sockfd, ] 255 | while True: 256 | try: 257 | rs, _, _ = select.select(fds, [], []) 258 | except select.error as e: 259 | print e 260 | sys.exit(-1) 261 | for fd in rs: 262 | if fd == sockfd: 263 | cs, ca = sock.accept() 264 | csfd = cs.fileno() 265 | fds.append(csfd) 266 | client = Transport(cs) 267 | client.set_tunfd(tunfd) 268 | clients[csfd] = client 269 | logger.info(u'Remote sock addr: [%s:%d]' % ca) 270 | elif fd == tunfd: 271 | logger.info(u'TUN dev recv, rs:[%r]' % rs) 272 | for client_fd in fds: 273 | if client_fd not in [tunfd, sockfd]: 274 | os.write(client_fd, os.read(tunfd, buflen)) 275 | else: 276 | rcv = os.read(fd, buflen) 277 | if len(rcv) == 0: 278 | print u'SOCK rcv [0]' 279 | fds.remove(fd) 280 | del clients[fd] 281 | continue 282 | logger.info(u'SOCK recv [%d]' % len(rcv)) 283 | client = clients[fd] 284 | client.recv(rcv) 285 | 286 | 287 | def main(): 288 | parser_config = { 289 | 'prog': 'pyvpn', 290 | 'description': 'VPN writen by python', 291 | 'version': PYVPN_VERSION, 292 | } 293 | parser = argparse.ArgumentParser(**parser_config) 294 | parser.add_argument('-s', '--server', nargs=2) 295 | parser.add_argument('-l', '--listen', nargs=2) 296 | parser.add_argument('-c', '--client', nargs=2) 297 | parser.add_argument('-r', '--remote', nargs=2) 298 | ns = parser.parse_args(sys.argv[1:]) 299 | if (ns.server and (ns.client or ns.remote) or 300 | ns.listen and (ns.client or ns.remote) or 301 | ns.client and (ns.server or ns.listen) or 302 | ns.remote and (ns.server or ns.listen)): 303 | print u'logistic error, client cannot running with server' 304 | parser.print_usage() 305 | sys.exit(1) 306 | if ns.server: 307 | gwip, netmask = ns.server 308 | lip, lport = ns.listen 309 | return server_main(gwip, netmask, lip, lport) 310 | elif ns.client: 311 | ip, netmask = ns.client 312 | host, port = ns.remote 313 | return client_main(ip, netmask, host, port) 314 | 315 | 316 | if __name__ == '__main__': 317 | main() 318 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | #PYVPN 2 | 3 | #通讯协议 4 | 两字节的包头区域 unsigned int,指示总长度 5 | 包体区域 6 | 1字节报类型 unsigned int 7 | 内容区域 8 | 一字节校验位 9 | 10 | ##认证协议 11 | 报类型 0 short int 12 | 1字节用户名长度 13 | 用户名 14 | 1字节密码长度 15 | 密码 16 | 17 | 返回: 18 | 报类型 0,标志位,成功0,失败1 19 | 1字节原因长度,原因 20 | ##心跳协议 21 | 报类型 1 22 | 返回 报类型1 23 | 24 | ##IP配置 25 | 报类型2 26 | 27 | 返回类型2 28 | IP 4字节 29 | 掩码 4字节 30 | 31 | ##数据包 32 | 报类型 3 33 | 内容 使用zlib压缩 34 | -------------------------------------------------------------------------------- /src/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | ''' 4 | Created on 2014年12月6日 5 | 6 | @author: Sunday 7 | server: 8 | eth0: 192.168.0.192/24, listen on eth0 23456, communication with tcp 9 | tun0: 192.168.10.1/24 10 | 11 | #TODO: 12 | 2, Data gzip 13 | 3, user auth 14 | 4, hub to switch 15 | 5, select to epoll 16 | 6, test global route 17 | 7, traffic counting 18 | 19 | protocol: 20 | ---------------------------- 21 | | | 22 | ---------------------------- 23 | header, body 24 | 1 byte, 4 byte, var byte 25 | data, push-ip, push-route, require-auth, auth-res 26 | ''' 27 | import fcntl # @UnresolvedImport 28 | import socket 29 | import select 30 | import os 31 | import logging 32 | import struct 33 | import time 34 | import sys 35 | logger = logging.getLogger('vpn') 36 | logger.addHandler(logging.StreamHandler()) 37 | logger.setLevel(logging.DEBUG) 38 | PYVPN_VERSION = '0.1' 39 | 40 | # find const values 41 | # grep IFF_UP -rl /usr/include/ 42 | IFF_UP = 0x1 43 | IFF_RUNNING = 0x40 44 | IFNAMSIZ = 16 45 | SIOCSIFADDR = 0x8916 46 | SIOCSIFNETMASK = 0x891c 47 | SIOCGIFFLAGS = 0x8913 48 | SIOCSIFFLAGS = 0x8914 49 | SIOCADDRT = 0x890B 50 | 51 | RTF_UP = 0x0001 52 | RTF_GATEWAY = 0x0002 53 | 54 | AF_INET = socket.AF_INET 55 | 56 | 57 | def to_int(s): 58 | try: 59 | return int(s) 60 | except ValueError as _unused: 61 | return None 62 | 63 | 64 | class exp_none(object): 65 | def __init__(self, fn): 66 | self.fn = fn 67 | 68 | def __call__(self, *args, **kwargs): 69 | try: 70 | return self.fn(*args, **kwargs) 71 | except Exception as e: 72 | logger.warn(e) 73 | return None 74 | 75 | 76 | def make_tun(): 77 | TUNSETIFF = 0x400454ca 78 | TUNSETOWNER = TUNSETIFF + 2 79 | IFF_TUN = 0x0001 80 | IFF_NO_PI = 0x1000 81 | 82 | # Open TUN device file. 83 | tun = open('/dev/net/tun', 'r+b') 84 | # Tall it we want a TUN device named tun0. 85 | ifr = struct.pack('16sH', 'tun%d', IFF_TUN | IFF_NO_PI) 86 | ret = fcntl.ioctl(tun, TUNSETIFF, ifr) 87 | dev, _ = struct.unpack('16sH', ret) 88 | dev = dev.strip() 89 | # Optionally, we want it be accessed by the normal user. 90 | fcntl.ioctl(tun, TUNSETOWNER, 1000) 91 | return dev, tun 92 | 93 | 94 | @exp_none 95 | def ifconfig(dev, ipaddr, netmask): 96 | # http://stackoverflow.com/questions/6652384/how-to-set-the-ip-address-from-c-in-linux 97 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_IP) 98 | AF_INET = socket.AF_INET 99 | fd = sock.fileno() 100 | addrbuf = struct.pack('BBBB', *[int(el) for el in ipaddr.split('.')]) 101 | maskbuf = struct.pack('BBBB', *[int(el) for el in netmask.split('.')]) 102 | sockaddr_mt = '16sHH4s' 103 | flags_mt = '16sH' 104 | # ADDR 105 | siocsifaddr = struct.pack(sockaddr_mt, dev, AF_INET, 0, addrbuf) 106 | fcntl.ioctl(fd, SIOCSIFADDR, siocsifaddr) 107 | # MASK 108 | siocsifnetmask = struct.pack(sockaddr_mt, dev, AF_INET, 0, maskbuf) 109 | fcntl.ioctl(fd, SIOCSIFNETMASK, siocsifnetmask) 110 | # ifconfig tun0 up 111 | ifr2 = struct.pack(flags_mt, dev, 0) 112 | ifr_ret = fcntl.ioctl(fd, SIOCGIFFLAGS, ifr2) 113 | cur_flags = struct.unpack(flags_mt, ifr_ret)[1] 114 | flags = cur_flags | (IFF_UP | IFF_RUNNING) 115 | ifr_ret = struct.pack(flags_mt, dev, flags) 116 | ifr_ret = fcntl.ioctl(fd, SIOCSIFFLAGS, ifr_ret) 117 | return 0 118 | 119 | 120 | @exp_none 121 | def add_route(dest, mask, gw): 122 | # sudo strace route add -net 192.168.0.0/24 gw 192.168.10.1 123 | # ioctl(3, SIOCADDRT, ifr) 124 | # /usr/include/net/route.h 125 | pad = '\x00' * 8 126 | inet_aton = socket.inet_aton 127 | sockaddr_in_fmt = 'hH4s8s' 128 | rtentry_fmt = 'L16s16s16sH38s' 129 | dst = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(dest), pad) 130 | next_gw = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(gw), pad) 131 | netmask = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(mask), pad) 132 | rt_flags = RTF_UP | RTF_GATEWAY 133 | rtentry = struct.pack(rtentry_fmt, 134 | 0, dst, next_gw, netmask, rt_flags, '\x00' * 38) 135 | sock = socket.socket(AF_INET, socket.SOCK_DGRAM, 0) 136 | fcntl.ioctl(sock.fileno(), SIOCADDRT, rtentry) 137 | return 0 138 | 139 | 140 | def conn_to_vpn(addr, port): 141 | sock = socket.socket() 142 | addr = (addr, port) 143 | try: 144 | sock.connect(addr) 145 | except socket.error as e: 146 | print 'Connect to VPN:[%d],[%s]' % (e.errno, e.strerror) 147 | return None 148 | sock.setblocking(False) 149 | return sock 150 | 151 | 152 | class Transport(object): 153 | def __init__(self, sock): 154 | self.sock = sock 155 | self.buf = '' 156 | 157 | def set_tunfd(self, tunfd): 158 | self.tunfd = tunfd 159 | 160 | def get_frame(self, buf): 161 | if len(buf) <= 20: 162 | return -1 163 | pack_len = struct.unpack('!H', buf[2:4])[0] 164 | logger.info('FRAME:[%d], BUF:[%d]' % (pack_len, len(buf))) 165 | if len(buf) < pack_len: 166 | return -1 167 | return pack_len 168 | 169 | def recv(self, buf): 170 | self.buf += buf 171 | while True: 172 | # 一次只能写入一个 IP包,帧。 173 | length = self.get_frame(self.buf) 174 | if length == -1: 175 | break 176 | frame = self.buf[:length] 177 | self.buf = self.buf[length:] 178 | os.write(self.tunfd, frame) 179 | logger.info('Write to TUN:[%d]' % len(frame)) 180 | 181 | 182 | def client_main(host, port, user, pwd): 183 | buflen = 65536 184 | dev, tundev = make_tun() 185 | tunfd = tundev.fileno() 186 | sock = conn_to_vpn(host, int(port)) 187 | logger.info(u'TUN dev OK, FD:[%d]' % tunfd) 188 | iret = ifconfig(dev, ip, netmask) 189 | if iret is None: 190 | logger.info(u'ip config %s error' % dev) 191 | return sys.exit(1) 192 | iret = add_route('192.168.0.0', '255.255.255.0', '192.168.10.1') 193 | if iret is None: 194 | logger.info(u'route config %s error' % dev) 195 | return sys.exit(1) 196 | time.sleep(1) 197 | 198 | sock = conn_to_vpn(host, int(port)) 199 | if sock is None: 200 | print u'SOCK dev Fail' 201 | sys.exit(-1) 202 | client = Transport(sock) 203 | client.set_tunfd(tunfd) 204 | sockfd = sock.fileno() 205 | logger.info(u'SOCK dev OK, FD:[%d]' % sockfd) 206 | 207 | fds = [tunfd, sockfd, ] 208 | while True: 209 | rs, _, _ = select.select(fds, [], []) 210 | for fd in rs: 211 | if fd == tunfd: 212 | rcv = os.read(tunfd, buflen) 213 | if len(rcv) == 0: 214 | logger.warn(u'TUN recv [0], Continue') 215 | continue 216 | sent_len = sock.send(rcv) 217 | logger.info('TUN recv, write to SOCK:[%r]' % sent_len) 218 | elif fd == sockfd: 219 | rcv = sock.recv(buflen) 220 | if len(rcv) == 0: 221 | logger.warn(u'SOCK recv [0], break') 222 | os.close(sockfd) 223 | break 224 | logger.info('SOCK recv [%d]' % len(rcv)) 225 | client.recv(rcv) 226 | 227 | 228 | def main(): 229 | return client_main('192.168.2.108', 1234, 'sunday', '12345678') 230 | 231 | 232 | if __name__ == '__main__': 233 | main() 234 | -------------------------------------------------------------------------------- /src/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | ''' 4 | Created on 2015年3月7日 5 | 6 | @author: Sunday 7 | ''' 8 | import os 9 | import zlib 10 | import struct 11 | import logging 12 | 13 | from twisted.internet.protocol import Factory 14 | from twisted.protocols.policies import TimeoutMixin 15 | from twisted.internet.abstract import FileDescriptor 16 | from twisted.internet import fdesc, protocol, reactor 17 | 18 | import util 19 | import webconsole 20 | 21 | logger = logging.getLogger("pyvpn") 22 | 23 | 24 | class AppException(Exception): 25 | pass 26 | 27 | 28 | class TundevException(AppException): 29 | pass 30 | 31 | 32 | class IpFullException(TundevException): 33 | pass 34 | 35 | 36 | class TunDevice(FileDescriptor, object): 37 | def __init__(self, reactor=None): 38 | FileDescriptor.__init__(self, reactor=reactor) 39 | self.dev, self._tun = util.make_tun() 40 | self.tunfd = self._tun.fileno() 41 | fdesc.setNonBlocking(self.tunfd) 42 | self._write_buf = '' 43 | 44 | def get_free_addr(self): 45 | addr = self._netaddr 46 | while True: 47 | addr = addr + 1 48 | if addr == self._gwaddr: 49 | continue 50 | if addr in self.allocated_addr: 51 | continue 52 | if addr >= self._boardcast: 53 | raise IpFullException("IP分配已满") 54 | self.allocated_addr.append(addr) 55 | return addr, util.inet_atol(self.netmask) 56 | 57 | def remove_addr(self, addr): 58 | self.allocated_addr.remove(addr) 59 | 60 | def ifconfig(self, gwaddr, netmask): 61 | assert util.is_valid_ip(gwaddr) 62 | assert util.is_valid_netmask(netmask) 63 | self.gwaddr = gwaddr 64 | self.netmask = netmask 65 | self._netaddr = util.addr_netaddr(self.gwaddr, self.netmask) 66 | self._boardcast = util.addr_boardcast(self.gwaddr, self.netmask) 67 | self._gwaddr = util.inet_atol(self.gwaddr) 68 | # 已分配地址,整形地址 69 | self.allocated_addr = [] 70 | util.ifconfig(self.dev, gwaddr, netmask) 71 | 72 | def fileno(self): 73 | return self.tunfd 74 | 75 | def _doRead(self, in_): 76 | ''' 77 | @summary: 操作系统发给虚拟网卡的数据包,拆出其目标地址,发给目标用户 78 | //先群发。 79 | :param data: 80 | ''' 81 | self.factory.tunRecv(in_) 82 | 83 | def doRead(self): 84 | print '~~~~~~~~~~~~~~~~~~~~~~~~~' 85 | fdesc.readFromFD(self.tunfd, self._doRead) 86 | print '~~~~~~~~~~~~~~~~~~~~~~~~~' 87 | 88 | @staticmethod 89 | def get_frame(buf): 90 | if len(buf) <= 20: 91 | return -1 92 | pack_len = struct.unpack('!H', buf[2:4])[0] 93 | logger.info('FRAME:[%d], BUF:[%d]' % (pack_len, len(buf))) 94 | if len(buf) < pack_len: 95 | return -1 96 | return pack_len 97 | 98 | def writeSomeData(self, data): 99 | self._write_buf += data 100 | while True: 101 | length = self.get_frame(self._write_buf) 102 | if length == -1: 103 | break 104 | frame = self._write_buf[:length] 105 | self._write_buf = self._write_buf[length:] 106 | fdesc.writeToFD(self.tunfd, frame) 107 | 108 | def connectionLost(self, reason): 109 | if self.tunfd >= 0: 110 | try: 111 | os.close(self.tunfd) 112 | except OSError as _: 113 | logger.error("cannot close tunfd") 114 | FileDescriptor.connectionLost(self, reason) 115 | 116 | 117 | class VPNProtocol(protocol.Protocol, TimeoutMixin): 118 | def connectionMade(self): 119 | self._is_authed = False 120 | self._is_gzip = True 121 | self._is_encrypt = False 122 | self._interval = 120 123 | self._client_addr = None 124 | self._client_mask = None 125 | self._flow_count = 0 126 | self.buf = '' 127 | self._user = '' 128 | self._addr, self._netmask = None, None 129 | self.setTimeout(self._interval) 130 | 131 | def connectionLost(self, reason): 132 | ''' 133 | @summary: 释放超时信号,释放已分配IP,写入流量等数据 134 | :param reason: 135 | ''' 136 | self.setTimeout(None) 137 | self.factory.clients.remove(self) 138 | if self._addr: 139 | self.tundev.remove_addr(self._addr) 140 | 141 | def getpackage(self): 142 | if len(self.buf) <= 2: 143 | return None 144 | d_length = struct.unpack('@H', self.buf[:2])[0] 145 | if len(self.buf) < d_length: 146 | return None 147 | pack = self.buf[:d_length] 148 | self.buf = self.buf[d_length:] 149 | return pack 150 | 151 | def user_auth(self, pack): 152 | user_len = struct.unpack('@b', pack[3])[0] 153 | assert user_len 154 | user = pack[4: 4 + user_len] 155 | pwd_len = struct.unpack('@b', pack[4 + user_len])[0] 156 | assert pwd_len 157 | pwd = pack[4 + user_len + 1:] 158 | assert user and pwd 159 | retpack = struct.pack('@Hbb', 4, 0, 0) 160 | self.transport.write(retpack) 161 | self._is_authed = True 162 | self._user = user 163 | return True 164 | 165 | def heartbeat(self): 166 | assert self._is_authed 167 | retpack = struct.pack('@Hb', 3, 1) 168 | self.transport.write(retpack) 169 | 170 | def peer_ifconfig(self): 171 | assert self._is_authed 172 | self._addr, self._netmask = self.tundev.get_free_addr() 173 | # 返回类型2, ip与掩码各4字节,总长度11字节 174 | retpack = struct.pack('@HbII', 11, 2, self._addr, self._netmask) 175 | self.transport.write(retpack) 176 | 177 | def trans_data(self, pack): 178 | assert self._is_authed 179 | raw_data = zlib.decompress(pack[3:]) 180 | self.tundev.writeSomeData(raw_data) 181 | 182 | def add_flow_count(self, count): 183 | self._flow_count += count 184 | 185 | def dataReceived(self, data): 186 | self.setTimeout(None) 187 | self.buf += data 188 | self._flow_count += len(data) 189 | while True: 190 | pack = self.getpackage() 191 | if not pack: 192 | break 193 | pack_type = struct.unpack('@b', pack[2])[0] 194 | if pack_type == util.PackageType.AUTH: 195 | self.user_auth(pack) 196 | elif pack_type == util.PackageType.HEARTBEAT: 197 | self.heartbeat() 198 | elif pack_type == util.PackageType.IFCONFIG: 199 | self.peer_ifconfig() 200 | elif pack_type == util.PackageType.DATA: 201 | self.trans_data(pack) 202 | 203 | 204 | class VPNFactory(Factory): 205 | def __init__(self, tundev): 206 | self.protocol = VPNProtocol 207 | self.tundev = tundev 208 | # 将工厂 赋值到 tundev,使之可以将数据群发出去 209 | self.tundev.factory = self 210 | self.clients = [] 211 | 212 | def tunRecv(self, in_): 213 | for client in self.clients: 214 | client.transport.write(in_) 215 | client.add_flow_count() 216 | 217 | def buildProtocol(self, addr): 218 | p = self.protocol() 219 | self.clients.append(p) 220 | p.factory = self 221 | p.tundev = self.tundev 222 | return p 223 | 224 | 225 | def main(): 226 | # TUN dev 227 | tundev = TunDevice(reactor) 228 | tundev.ifconfig('192.168.10.3', '255.255.255.240') 229 | tundev.startReading() 230 | # tcp for client 231 | serverfactory = VPNFactory(tundev) 232 | reactor.listenTCP(1234, serverfactory) # @UndefinedVariable 233 | # web console 234 | reactor.listenTCP(8080, webconsole.factory) # @UndefinedVariable 235 | # run 236 | reactor.run() # @UndefinedVariable 237 | 238 | 239 | if __name__ == '__main__': 240 | main() 241 | 242 | 243 | # TODO: 244 | r""" 245 | 1, VPN 246 | 1.1, 自动的IP配置 247 | 2, Win32 client 248 | 2.1, Android client 249 | 3, 简单的认证机制,地址端口用户名密码 250 | 4, 压缩与可选的加密机制 251 | 5, 流量控制 252 | 6, 限速 253 | 7, Web控制与状态显示 254 | LAST, 性能 255 | """ -------------------------------------------------------------------------------- /src/test_client_protocol.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | ''' 3 | @author: Sunday 4 | ''' 5 | from socket import socket 6 | import struct 7 | 8 | 9 | def get_conn(): 10 | sock = socket() 11 | addr = ('192.168.2.108', 1234) 12 | sock.connect(addr) 13 | return sock 14 | 15 | 16 | def recv_body(sock): 17 | head_buf = sock.recv(2) 18 | length = struct.unpack('@H', head_buf)[0] 19 | return sock.recv(length) 20 | 21 | 22 | def auth(user, pwd): 23 | sock = get_conn() 24 | # 2 + 1 + 1 + len(user) + 1 + len(pwd) 25 | body = chr(0) + chr(len(user)) + user + chr(len(pwd)) + pwd 26 | head = struct.pack('@H', len(body) + 2) 27 | sock.send(head + body) 28 | retbody = recv_body(sock) 29 | print repr(retbody) 30 | return sock 31 | 32 | 33 | def test_auth(): 34 | auth('sunday', '12345678') 35 | 36 | 37 | def test_getip(): 38 | # sock = auth('sunday', '12345678') 39 | sock = get_conn() 40 | body = chr(2) 41 | head = struct.pack('@H', len(body) + 2) 42 | sock.send(head + body) 43 | retbody = recv_body(sock) 44 | print repr(retbody) 45 | return sock 46 | 47 | 48 | if __name__ == '__main__': 49 | # test_auth() 50 | test_getip() 51 | -------------------------------------------------------------------------------- /src/util.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | ''' 3 | Created on 2015年3月7日 4 | 5 | @author: Sunday 6 | ''' 7 | import fcntl # @UnresolvedImport 8 | import socket 9 | import logging 10 | import struct 11 | logger = logging.getLogger('vpn') 12 | logger.addHandler(logging.StreamHandler()) 13 | logger.setLevel(logging.DEBUG) 14 | PYVPN_VERSION = '0.1' 15 | 16 | # find const values 17 | # grep IFF_UP -rl /usr/include/ 18 | IFF_UP = 0x1 19 | IFF_RUNNING = 0x40 20 | IFNAMSIZ = 16 21 | SIOCSIFADDR = 0x8916 22 | SIOCSIFNETMASK = 0x891c 23 | SIOCGIFFLAGS = 0x8913 24 | SIOCSIFFLAGS = 0x8914 25 | SIOCADDRT = 0x890B 26 | 27 | RTF_UP = 0x0001 28 | RTF_GATEWAY = 0x0002 29 | 30 | AF_INET = socket.AF_INET 31 | 32 | 33 | def to_int(s): 34 | try: 35 | return int(s) 36 | except ValueError as _unused: 37 | return None 38 | 39 | 40 | class exp_none(object): 41 | def __init__(self, fn): 42 | self.fn = fn 43 | 44 | def __call__(self, *args, **kwargs): 45 | try: 46 | return self.fn(*args, **kwargs) 47 | except Exception as e: 48 | logger.warn(e) 49 | return None 50 | 51 | 52 | def make_tun(): 53 | TUNSETIFF = 0x400454ca 54 | TUNSETOWNER = TUNSETIFF + 2 55 | IFF_TUN = 0x0001 56 | IFF_NO_PI = 0x1000 57 | 58 | # Open TUN device file. 59 | tun = open('/dev/net/tun', 'r+b') 60 | # Tall it we want a TUN device named tun0. 61 | ifr = struct.pack('16sH', 'tun%d', IFF_TUN | IFF_NO_PI) 62 | ret = fcntl.ioctl(tun, TUNSETIFF, ifr) 63 | dev, _ = struct.unpack('16sH', ret) 64 | dev = dev.strip() 65 | # Optionally, we want it be accessed by the normal user. 66 | fcntl.ioctl(tun, TUNSETOWNER, 1000) 67 | return dev, tun 68 | 69 | 70 | @exp_none 71 | def ifconfig(dev, ipaddr, netmask): 72 | # http://stackoverflow.com/questions/6652384/how-to-set-the-ip-address-from-c-in-linux 73 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_IP) 74 | AF_INET = socket.AF_INET 75 | fd = sock.fileno() 76 | addrbuf = struct.pack('BBBB', *[int(el) for el in ipaddr.split('.')]) 77 | maskbuf = struct.pack('BBBB', *[int(el) for el in netmask.split('.')]) 78 | sockaddr_mt = '16sHH4s' 79 | flags_mt = '16sH' 80 | # ADDR 81 | siocsifaddr = struct.pack(sockaddr_mt, dev, AF_INET, 0, addrbuf) 82 | fcntl.ioctl(fd, SIOCSIFADDR, siocsifaddr) 83 | # MASK 84 | siocsifnetmask = struct.pack(sockaddr_mt, dev, AF_INET, 0, maskbuf) 85 | fcntl.ioctl(fd, SIOCSIFNETMASK, siocsifnetmask) 86 | # ifconfig tun0 up 87 | ifr2 = struct.pack(flags_mt, dev, 0) 88 | ifr_ret = fcntl.ioctl(fd, SIOCGIFFLAGS, ifr2) 89 | cur_flags = struct.unpack(flags_mt, ifr_ret)[1] 90 | flags = cur_flags | (IFF_UP | IFF_RUNNING) 91 | ifr_ret = struct.pack(flags_mt, dev, flags) 92 | ifr_ret = fcntl.ioctl(fd, SIOCSIFFLAGS, ifr_ret) 93 | return 0 94 | 95 | 96 | @exp_none 97 | def add_route(dest, mask, gw): 98 | # sudo strace route add -net 192.168.0.0/24 gw 192.168.10.1 99 | # ioctl(3, SIOCADDRT, ifr) 100 | # /usr/include/net/route.h 101 | pad = '\x00' * 8 102 | inet_aton = socket.inet_aton 103 | sockaddr_in_fmt = 'hH4s8s' 104 | rtentry_fmt = 'L16s16s16sH38s' 105 | dst = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(dest), pad) 106 | next_gw = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(gw), pad) 107 | netmask = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(mask), pad) 108 | rt_flags = RTF_UP | RTF_GATEWAY 109 | rtentry = struct.pack(rtentry_fmt, 110 | 0, dst, next_gw, netmask, rt_flags, '\x00' * 38) 111 | sock = socket.socket(AF_INET, socket.SOCK_DGRAM, 0) 112 | fcntl.ioctl(sock.fileno(), SIOCADDRT, rtentry) 113 | return 0 114 | 115 | 116 | def enable_tcp_forward(): 117 | logger.info(u'Set ip_forward=1') 118 | with open('/proc/sys/net/ipv4/ip_forward', 'wb+') as f1: 119 | f1.seek(0) 120 | f1.write('1') 121 | 122 | 123 | def inet_ltoa(addr_long): 124 | ''' 125 | @summary: 转换 整数 到字符串的IP地址 126 | :param addr_long: 整数地址,可以直接被ping的地址 127 | ''' 128 | return socket.inet_ntoa(struct.pack('!I', addr_long)) 129 | 130 | 131 | def inet_atol(addr): 132 | ''' 133 | @summary: 转换字符串IP地址到整数地址 134 | :param addr: like '192.168.2.121' 135 | ''' 136 | return struct.unpack('!I', socket.inet_aton(addr))[0] 137 | 138 | 139 | def is_valid_netmask(mask): 140 | ''' 141 | @summary: 校验是否为有效 掩码地址 142 | :param mask: 字符串类型的掩码地址如 255.255.255.128 => True 143 | // 255.255.0.255 => False 144 | ''' 145 | all_mask = [0xffffffff ^ (0xffffffff >> i) for i in range(32)] 146 | return mask in [inet_ltoa(el) for el in all_mask] 147 | 148 | 149 | def is_valid_ip(ip): 150 | """Returns true if the given string is a well-formed IP address. 151 | 152 | Supports IPv4 and IPv6. 153 | //取自 tornado 154 | """ 155 | if not ip or '\x00' in ip: 156 | # getaddrinfo resolves empty strings to localhost, and truncates 157 | # on zero bytes. 158 | return False 159 | try: 160 | res = socket.getaddrinfo(ip, 0, socket.AF_UNSPEC, 161 | socket.SOCK_STREAM, 162 | 0, socket.AI_NUMERICHOST) 163 | return bool(res) 164 | except socket.gaierror as e: 165 | if e.args[0] == socket.EAI_NONAME: 166 | return False 167 | raise 168 | return True 169 | 170 | 171 | def addr_netaddr(addr, netmask): 172 | ''' 173 | @summary: 获得某IP地址的网络地址,如 192.168.3.1, 255.255.255.0 => 192.168.3.0 174 | :param addr: like '192.168.0.23' 175 | :param netmask: like '255.255.255.0' 176 | @return: 整形地址 177 | ''' 178 | return inet_atol(addr) & inet_atol(netmask) 179 | 180 | 181 | def addr_boardcast(addr, netmask): 182 | ''' 183 | @summary: 获得某IP的广播地址,192.168.3.123, 255.255.255.0 => 192.168.3.255 184 | //网络地址是该子网的最小地址,广播地址是该子网的最大地址,中间除却网关地址后剩余可自由分配的其他地址 185 | :param addr: 186 | :param netmask: 187 | ''' 188 | return inet_atol(netmask) ^ 0xffffffff | inet_atol(addr) 189 | 190 | 191 | class PackageType(object): 192 | AUTH = 0 193 | HEARTBEAT = 1 194 | IFCONFIG = 2 195 | DATA = 3 196 | 197 | 198 | class User(object): 199 | def __init__(self): 200 | self.flow_count = 0 201 | self.addr = '' 202 | 203 | 204 | gl_userlist = {} 205 | 206 | __all__ = ['to_int', 'exp_none', 'make_tun', 207 | 'ifconfig', 'add_route', 'enable_tcp_forward', 208 | 'PackageType', 'User', 'gl_userlist'] 209 | -------------------------------------------------------------------------------- /src/webconsole.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | ''' 3 | Created on 2015年3月15日 4 | 5 | @author: Sunday 6 | ''' 7 | from twisted.web.resource import Resource 8 | 9 | root = Resource() 10 | 11 | 12 | if __name__ == '__main__': 13 | pass 14 | else: 15 | __all__ = ['factory', ] -------------------------------------------------------------------------------- /tun-ping-responder.py: -------------------------------------------------------------------------------- 1 | ## 2 | # This script attaches to a tun interface and answer IPv6 and IPv4 echo 3 | # requests ("ping"). 4 | # 5 | # This is companion script to the "tun/tap in Windows" at openwsn.org. 6 | # 7 | # It is an extended version of the following code: 8 | # - https://gist.github.com/glacjay/586892 9 | # - http://www.varsanofiev.com/inside/using_tuntap_under_windows.htm 10 | # 11 | # \author Thomas Watteyne , March 2013. 12 | # 13 | # The OpenWSN license applies to this file. 14 | # 15 | 16 | import _winreg as reg 17 | import win32file 18 | import win32event 19 | import pywintypes 20 | import threading 21 | import time 22 | 23 | #============================ defines ========================================= 24 | 25 | ## IPv4 configuration of your TUN interface (represented as a list of integers) 26 | TUN_IPv4_ADDRESS = [ 10, 2,0,1] ##< The IPv4 address of the TUN interface. 27 | TUN_IPv4_NETWORK = [ 10, 2,0,0] ##< The IPv4 address of the TUN interface's network. 28 | TUN_IPv4_NETMASK = [255,255,0,0] ##< The IPv4 netmask of the TUN interface. 29 | 30 | ## Key in the Windows registry where to find all network interfaces (don't change, this is always the same) 31 | ADAPTER_KEY = r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}' 32 | 33 | ## Value of the ComponentId key in the registry corresponding to your TUN interface. 34 | TUNTAP_COMPONENT_ID = 'tap0901' 35 | 36 | #============================ helpers ========================================= 37 | 38 | #=== tun/tap-related functions 39 | 40 | def get_tuntap_ComponentId(): 41 | ''' 42 | \brief Retrieve the instance ID of the TUN/TAP interface from the Windows 43 | registry, 44 | 45 | This function loops through all the sub-entries at the following location 46 | in the Windows registry: reg.HKEY_LOCAL_MACHINE, ADAPTER_KEY 47 | 48 | It looks for one which has the 'ComponentId' key set to 49 | TUNTAP_COMPONENT_ID, and returns the value of the 'NetCfgInstanceId' key. 50 | 51 | \return The 'ComponentId' associated with the TUN/TAP interface, a string 52 | of the form "{A9A413D7-4D1C-47BA-A3A9-92F091828881}". 53 | ''' 54 | with reg.OpenKey(reg.HKEY_LOCAL_MACHINE, ADAPTER_KEY) as adapters: 55 | try: 56 | for i in xrange(10000): 57 | key_name = reg.EnumKey(adapters, i) 58 | with reg.OpenKey(adapters, key_name) as adapter: 59 | try: 60 | component_id = reg.QueryValueEx(adapter, 'ComponentId')[0] 61 | if component_id == TUNTAP_COMPONENT_ID: 62 | return reg.QueryValueEx(adapter, 'NetCfgInstanceId')[0] 63 | except WindowsError, err: 64 | pass 65 | except WindowsError, err: 66 | pass 67 | 68 | def CTL_CODE(device_type, function, method, access): 69 | return (device_type << 16) | (access << 14) | (function << 2) | method; 70 | 71 | def TAP_CONTROL_CODE(request, method): 72 | return CTL_CODE(34, request, method, 0) 73 | 74 | TAP_IOCTL_SET_MEDIA_STATUS = TAP_CONTROL_CODE( 6, 0) 75 | TAP_IOCTL_CONFIG_TUN = TAP_CONTROL_CODE(10, 0) 76 | 77 | def openTunTap(): 78 | ''' 79 | \brief Open a TUN/TAP interface and switch it to TUN mode. 80 | 81 | \return The handler of the interface, which can be used for later 82 | read/write operations. 83 | ''' 84 | 85 | # retrieve the ComponentId from the TUN/TAP interface 86 | componentId = get_tuntap_ComponentId() 87 | print('componentId = {0}'.format(componentId)) 88 | 89 | # create a win32file for manipulating the TUN/TAP interface 90 | tuntap = win32file.CreateFile( 91 | r'\\.\Global\%s.tap' % componentId, 92 | win32file.GENERIC_READ | win32file.GENERIC_WRITE, 93 | win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE, 94 | None, 95 | win32file.OPEN_EXISTING, 96 | win32file.FILE_ATTRIBUTE_SYSTEM | win32file.FILE_FLAG_OVERLAPPED, 97 | None 98 | ) 99 | print('tuntap = {0}'.format(tuntap.handle)) 100 | 101 | # have Windows consider the interface now connected 102 | win32file.DeviceIoControl( 103 | tuntap, 104 | TAP_IOCTL_SET_MEDIA_STATUS, 105 | '\x00\x00\x00\x00', 106 | None 107 | ) 108 | 109 | # prepare the parameter passed to the TAP_IOCTL_CONFIG_TUN commmand. 110 | # This needs to be a 12-character long string representing 111 | # - the tun interface's IPv4 address (4 characters) 112 | # - the tun interface's IPv4 network address (4 characters) 113 | # - the tun interface's IPv4 network mask (4 characters) 114 | configTunParam = [] 115 | configTunParam += TUN_IPv4_ADDRESS 116 | configTunParam += TUN_IPv4_NETWORK 117 | configTunParam += TUN_IPv4_NETMASK 118 | configTunParam = ''.join([chr(b) for b in configTunParam]) 119 | 120 | # switch to TUN mode (by default the interface runs in TAP mode) 121 | win32file.DeviceIoControl( 122 | tuntap, 123 | TAP_IOCTL_CONFIG_TUN, 124 | configTunParam, 125 | None 126 | ) 127 | 128 | # return the handler of the TUN interface 129 | return tuntap 130 | 131 | #=== misc 132 | 133 | def formatByteList(byteList): 134 | ''' 135 | \brief Format a byte list into a string, which can then be printed. 136 | 137 | For example: 138 | [0x00,0x11,0x22] -> '(3 bytes) 001122' 139 | 140 | \param[in] byteList A list of integer, each representing a byte. 141 | 142 | \return A string representing the byte list. 143 | ''' 144 | return '({0} bytes) {1}'.format(len(byteList),''.join(['%02x'%b for b in byteList])) 145 | 146 | def carry_around_add(a, b): 147 | ''' 148 | \brief Helper function for checksum calculation. 149 | ''' 150 | c = a + b 151 | return (c & 0xffff) + (c >> 16) 152 | 153 | def checksum(byteList): 154 | ''' 155 | \brief Calculate the checksum over a byte list. 156 | 157 | This is the checksum calculation used in e.g. the ICMPv6 header. 158 | 159 | \return The checksum, a 2-byte integer. 160 | ''' 161 | s = 0 162 | for i in range(0, len(byteList), 2): 163 | w = byteList[i] + (byteList[i+1] << 8) 164 | s = carry_around_add(s, w) 165 | return ~s & 0xffff 166 | 167 | #============================ threads ========================================= 168 | 169 | class ReadThread(threading.Thread): 170 | ''' 171 | \brief Thread which continously reads input from a TUN interface. 172 | 173 | If that input is an IPv4 or IPv6 echo request (a "ping" command) issued to 174 | any IP address in the virtual network behind the TUN interface, this thread 175 | answers with the appropriate echo reply. 176 | ''' 177 | 178 | ETHERNET_MTU = 1500 179 | IPv6_HEADER_LENGTH = 40 180 | 181 | def __init__(self,tuntap,transmit): 182 | 183 | # store params 184 | self.tuntap = tuntap 185 | self.transmit = transmit 186 | 187 | # local variables 188 | self.goOn = True 189 | self.overlappedRx = pywintypes.OVERLAPPED() 190 | self.overlappedRx.hEvent = win32event.CreateEvent(None, 0, 0, None) 191 | 192 | # initialize parent 193 | threading.Thread.__init__(self) 194 | 195 | # give this thread a name 196 | self.name = 'readThread' 197 | 198 | def run(self): 199 | 200 | rxbuffer = win32file.AllocateReadBuffer(self.ETHERNET_MTU) 201 | 202 | while self.goOn: 203 | 204 | # wait for data 205 | l, p = win32file.ReadFile(self.tuntap, rxbuffer, self.overlappedRx) 206 | win32event.WaitForSingleObject(self.overlappedRx.hEvent, win32event.INFINITE) 207 | self.overlappedRx.Offset = self.overlappedRx.Offset + len(p) 208 | 209 | # convert input from a string to a byte list 210 | p = [ord(b) for b in p] 211 | 212 | # print input 213 | #print 'in: l: {0} p: {1}'.format(l,formatByteList(p)) 214 | 215 | # parse received packet 216 | if (p[0]&0xf0)==0x40: 217 | # IPv4 218 | 219 | # keep only IPv4 packet 220 | total_length = 256*p[2]+p[3] 221 | p = p[:total_length] 222 | 223 | if p[9]==0x01: 224 | # ICMPv4 225 | 226 | if p[20]==0x08: 227 | # IPv4 echo request 228 | 229 | # print 230 | print 'Received IPv4 echo request' 231 | 232 | # create echo reply 233 | echoReply = self._createIpv4EchoReply(p) 234 | 235 | # send over interface 236 | self.transmit(echoReply) 237 | 238 | # print 239 | print 'Transmitted IPv4 echo reply' 240 | 241 | elif p[20]==0x00: 242 | 243 | # print 244 | print 'Received IPv4 echo reply' 245 | 246 | elif (p[0]&0xf0)==0x60: 247 | # IPv6 248 | 249 | # keep only IPv6 packet 250 | payload_length = 256*p[4]+p[5] 251 | p = p[:payload_length+self.IPv6_HEADER_LENGTH] 252 | 253 | if p[6]==0x3a: 254 | # ICMPv6 255 | 256 | if p[40]==0x80: 257 | # IPv6 echo request 258 | 259 | # print 260 | print 'Received IPv6 echo request' 261 | 262 | # create echo reply 263 | echoReply = self._createIpv6EchoReply(p) 264 | 265 | # send over interface 266 | self.transmit(echoReply) 267 | 268 | # print 269 | print 'Transmitted IPv6 echo reply' 270 | 271 | elif p[40]==0x81: 272 | 273 | # print 274 | print 'Received IPv6 echo reply' 275 | 276 | #======================== public ========================================== 277 | 278 | def close(self): 279 | self.goOn = False 280 | 281 | #======================== private ========================================= 282 | 283 | def _createIpv4EchoReply(self,echoRequest): 284 | 285 | # invert addresses, change "echo request" type to "echo reply" 286 | echoReply = echoRequest[:12] + \ 287 | echoRequest[16:20] + \ 288 | echoRequest[12:16] + \ 289 | [0x00] + \ 290 | echoRequest[21:] 291 | 292 | # recalculate checksum 293 | echoReply[22] = 0x00 294 | echoReply[23] = 0x00 295 | crc = checksum(echoReply[20:]) 296 | echoReply[22] = (crc&0x00ff)>>0 297 | echoReply[23] = (crc&0xff00)>>8 298 | 299 | return echoReply 300 | 301 | def _createIpv6EchoReply(self,echoRequest): 302 | 303 | # invert addresses, change "echo request" type to "echo reply" 304 | echoReply = echoRequest[:8] + \ 305 | echoRequest[24:40] + \ 306 | echoRequest[8:24] + \ 307 | [129] + \ 308 | echoRequest[41:] 309 | 310 | # recalculate checksum 311 | pseudo = [] 312 | pseudo += echoRequest[24:40] # source address 313 | pseudo += echoRequest[8:24] # destination address 314 | pseudo += [0x00]*3+[len(echoRequest[40:])] # upper-layer packet length 315 | pseudo += [0x00]*3 # zero 316 | pseudo += [58] # next header 317 | pseudo += echoRequest[40:] # ICMPv6 header+payload 318 | 319 | pseudo[40] = 129 # ICMPv6 type = echo reply 320 | pseudo[42] = 0x00 # reset CRC for calculation 321 | pseudo[43] = 0x00 # reset CRC for calculation 322 | 323 | crc = checksum(pseudo) 324 | 325 | echoReply[42] = (crc&0x00ff)>>0 326 | echoReply[43] = (crc&0xff00)>>8 327 | 328 | return echoReply 329 | 330 | class WriteThread(threading.Thread): 331 | ''' 332 | \brief Thread with periodically sends IPv4 and IPv6 echo requests. 333 | ''' 334 | 335 | SLEEP_PERIOD = 1 336 | 337 | def __init__(self,tuntap): 338 | 339 | # store params 340 | self.tuntap = tuntap 341 | 342 | # local variables 343 | self.goOn = True 344 | self.createIPv6 = False 345 | self.overlappedTx = pywintypes.OVERLAPPED() 346 | self.overlappedTx.hEvent = win32event.CreateEvent(None, 0, 0, None) 347 | 348 | # initialize parent 349 | threading.Thread.__init__(self) 350 | 351 | # give this thread a name 352 | self.name = 'writeThread' 353 | 354 | def run(self): 355 | 356 | while self.goOn: 357 | 358 | # sleep a bit 359 | time.sleep(self.SLEEP_PERIOD) 360 | 361 | # create an echo request 362 | dataToTransmit = self._createEchoRequest() 363 | 364 | # transmit 365 | self.transmit(dataToTransmit) 366 | 367 | #======================== public ========================================== 368 | 369 | def close(self): 370 | self.goOn = False 371 | 372 | def transmit(self,dataToTransmit): 373 | 374 | # convert to string 375 | dataToTransmit = ''.join([chr(b) for b in dataToTransmit]) 376 | 377 | # write over tuntap interface 378 | win32file.WriteFile(self.tuntap, dataToTransmit, self.overlappedTx) 379 | win32event.WaitForSingleObject(self.overlappedTx.hEvent, win32event.INFINITE) 380 | self.overlappedTx.Offset = self.overlappedTx.Offset + len(dataToTransmit) 381 | 382 | #======================== private ========================================= 383 | 384 | def _createEchoRequest(self): 385 | ''' 386 | \brief Create an echo request. 387 | 388 | This function switches between IPv4 and IPv6 echo requests. 389 | ''' 390 | 391 | # toggle createIPv6 flag 392 | self.createIPv6 = not self.createIPv6 393 | 394 | # create IPv4 or IPv6 echo request 395 | if self.createIPv6: 396 | print 'Transmitting IPv6 echo request' 397 | return self._createIPv6echoRequest() 398 | else: 399 | print 'Transmitting IPv4 echo request' 400 | return self._createIPv4echoRequest() 401 | 402 | def _createIPv4echoRequest(self): 403 | ''' 404 | \brief Create a value IPv4 echo request. 405 | ''' 406 | 407 | echoRequest = [] 408 | 409 | # IPv4 header 410 | echoRequest += [0x45] # Version | IHL 411 | echoRequest += [0x00] # DSCP | ECN 412 | echoRequest += [0x00,60] # Total Length (20 for IPv4 + 40 ICMPv4) 413 | echoRequest += [0x00,0x00] # Identification 414 | echoRequest += [0x00,0x00] # Flags | Fragment Offset 415 | echoRequest += [128] # TTL 416 | echoRequest += [1] # Protocol (1==ICMP) 417 | echoRequest += [0x00,0x00] # Header Checksum (to be filled out later) 418 | echoRequest += [10,2,0,5] # Source IP 419 | echoRequest += [10,2,0,1] # Destination IP 420 | 421 | # calculate IPv4 Header checksum 422 | crc = checksum(echoRequest) 423 | echoRequest[10] = (crc&0x00ff)>>0 424 | echoRequest[11] = (crc&0xff00)>>8 425 | 426 | # ICMPv4 header 427 | echoRequest += [8] # type (8==echo request) 428 | echoRequest += [0] # code 429 | echoRequest += [0x00,0x00] # Checksum (to be filled out later) 430 | echoRequest += [0x00,0x00] # Identifier 431 | echoRequest += [0x00,0x00] # Sequence Number 432 | 433 | # ICMPv4 payload 434 | echoRequest += [ord('a')+b for b in range(32)] 435 | 436 | # calculate ICMPv4 checksum 437 | crc = checksum(echoRequest[20:]) 438 | echoRequest[22] = (crc&0x00ff)>>0 439 | echoRequest[23] = (crc&0xff00)>>8 440 | 441 | return echoRequest 442 | 443 | def _createIPv6echoRequest(self): 444 | ''' 445 | \brief Create an IPv6 echo request. 446 | ''' 447 | 448 | echoRequest = [] 449 | 450 | # IPv6 header 451 | echoRequest += [0x60,0x00,0x00,0x00] # ver, TF 452 | echoRequest += [0x00, 40] # length 453 | echoRequest += [58] # Next header (58==ICMPv6) 454 | echoRequest += [128] # HLIM 455 | echoRequest += [0xbb, 0xbb, 0x00, 0x00, 456 | 0x00, 0x00, 0x00, 0x00, 457 | 0x00, 0x00, 0x00, 0x00, 458 | 0x00, 0x00, 0x00, 0x05,] # source 459 | echoRequest += [0xbb, 0xbb, 0x00, 0x00, 460 | 0x00, 0x00, 0x00, 0x00, 461 | 0x00, 0x00, 0x00, 0x00, 462 | 0x00, 0x00, 0x00, 0x01,] # destination 463 | 464 | # ICMPv6 header 465 | echoRequest += [128] # type (128==echo request) 466 | echoRequest += [0] # code 467 | echoRequest += [0x00,0x00] # Checksum (to be filled out later) 468 | echoRequest += [0x00,0x04] # Identifier 469 | echoRequest += [0x00,0x12] # Sequence 470 | 471 | # ICMPv6 payload 472 | echoRequest += [ord('a')+b for b in range(32)] 473 | 474 | # calculate ICMPv6 checksum 475 | pseudo = [] 476 | pseudo += echoRequest[24:40] # source address 477 | pseudo += echoRequest[8:24] # destination address 478 | pseudo += [0x00]*3+[len(echoRequest[40:])] # upper-layer packet length 479 | pseudo += [0x00]*3 # zero 480 | pseudo += [58] # next header 481 | pseudo += echoRequest[40:] # ICMPv6 header+payload 482 | 483 | crc = checksum(pseudo) 484 | 485 | echoRequest[42] = (crc&0x00ff)>>0 486 | echoRequest[43] = (crc&0xff00)>>8 487 | 488 | return echoRequest 489 | 490 | #============================ main ============================================ 491 | 492 | def main(): 493 | 494 | #=== open the TUN/TAP interface 495 | 496 | tuntap = openTunTap() 497 | 498 | #=== start read/write threads 499 | 500 | writeThread = WriteThread(tuntap) 501 | readThread = ReadThread(tuntap,writeThread.transmit) 502 | 503 | readThread.start() 504 | writeThread.start() 505 | 506 | #=== wait for Enter to stop 507 | 508 | raw_input("Press enter to stop...\n") 509 | 510 | readThread.close() 511 | writeThread.close() 512 | win32file.CloseHandle(tuntap) 513 | 514 | if __name__ == '__main__': 515 | main() 516 | -------------------------------------------------------------------------------- /tun-ping-win.py: -------------------------------------------------------------------------------- 1 | import _winreg as reg 2 | import win32file 3 | 4 | 5 | adapter_key = r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}' 6 | 7 | 8 | def get_device_guid(): 9 | with reg.OpenKey(reg.HKEY_LOCAL_MACHINE, adapter_key) as adapters: 10 | try: 11 | for i in xrange(10000): 12 | key_name = reg.EnumKey(adapters, i) 13 | with reg.OpenKey(adapters, key_name) as adapter: 14 | try: 15 | component_id = reg.QueryValueEx(adapter, 'ComponentId')[0] 16 | # print component_id 17 | # if component_id == 'tap0801': 18 | if component_id == 'tap0901': 19 | regid = reg.QueryValueEx(adapter, 'NetCfgInstanceId')[0] 20 | return 'regid:', regid 21 | except WindowsError, err: 22 | pass 23 | except WindowsError, err: 24 | pass 25 | 26 | def CTL_CODE(device_type, function, method, access): 27 | return (device_type << 16) | (access << 14) | (function << 2) | method; 28 | 29 | def TAP_CONTROL_CODE(request, method): 30 | return CTL_CODE(34, request, method, 0) 31 | 32 | TAP_IOCTL_CONFIG_POINT_TO_POINT = TAP_CONTROL_CODE(5, 0) 33 | TAP_IOCTL_SET_MEDIA_STATUS = TAP_CONTROL_CODE(6, 0) 34 | TAP_IOCTL_CONFIG_TUN = TAP_CONTROL_CODE(10, 0) 35 | 36 | 37 | if __name__ == '__main__': 38 | guid = get_device_guid() 39 | print 'guid: ', guid 40 | guid = guid[1] 41 | handle = win32file.CreateFile(r'\\.\Global\%s.tap' % guid, 42 | win32file.GENERIC_READ | win32file.GENERIC_WRITE, 43 | 0, #win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE, 44 | None, win32file.OPEN_EXISTING, 45 | win32file.FILE_ATTRIBUTE_SYSTEM | win32file.FILE_FLAG_OVERLAPPED, 46 | None) 47 | print(handle.handle) 48 | if False: 49 | win32file.DeviceIoControl(handle, TAP_IOCTL_CONFIG_POINT_TO_POINT, 50 | '\xc0\xa8\x11\x01\xc0\xa8\x11\x10', None); 51 | else: 52 | from IPython import embed 53 | embed() 54 | # win32file.DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS, '\x01\x00\x00\x00', None) 55 | win32file.DeviceIoControl(handle, TAP_IOCTL_CONFIG_TUN, 56 | '\x0a\x03\x00\x01\x0a\x03\x00\x00\xff\xff\xff\x00', None) 57 | while True: 58 | l, p = win32file.ReadFile(handle, 2000) 59 | q = p[:12] + p[16:20] + p[12:16] + p[20:] 60 | win32file.WriteFile(handle, q) 61 | print(p, q) 62 | win32file.CloseHandle(handle) 63 | -------------------------------------------------------------------------------- /tuntest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | ''' 4 | Created on 2014年12月14日 5 | 6 | @author: Sunday 7 | ''' 8 | import fcntl # @UnresolvedImport 9 | import logging 10 | import struct 11 | import os 12 | import socket 13 | from IPython import embed 14 | logger = logging.getLogger('vpn') 15 | logger.addHandler(logging.StreamHandler()) 16 | logger.setLevel(logging.DEBUG) 17 | PYVPN_VERSION = '0.1' 18 | 19 | IFF_UP = 0x1 20 | IFF_RUNNING = 0x40 21 | IFNAMSIZ = 16 22 | SIOCSIFADDR = 0x8916 23 | SIOCSIFNETMASK = 0x891c 24 | SIOCGIFFLAGS = 0x8913 25 | SIOCSIFFLAGS = 0x8914 26 | SIOCADDRT = 0x890B 27 | RTF_UP = 0x0001 28 | RTF_GATEWAY = 0x0002 29 | 30 | AF_INET = socket.AF_INET 31 | 32 | 33 | def ifconfig(dev, ipaddr, netmask): 34 | # http://stackoverflow.com/questions/6652384/how-to-set-the-ip-address-from-c-in-linux 35 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_IP) 36 | AF_INET = socket.AF_INET 37 | fd = sock.fileno() 38 | addrbuf = struct.pack('BBBB', *[int(el) for el in ipaddr.split('.')]) 39 | maskbuf = struct.pack('BBBB', *[int(el) for el in netmask.split('.')]) 40 | sockaddr_mt = '16sHH4s' 41 | flags_mt = '16sH' 42 | # ADDR 43 | siocsifaddr = struct.pack(sockaddr_mt, dev, AF_INET, 0, addrbuf) 44 | fcntl.ioctl(fd, SIOCSIFADDR, siocsifaddr) 45 | # MASK 46 | siocsifnetmask = struct.pack(sockaddr_mt, dev, AF_INET, 0, maskbuf) 47 | fcntl.ioctl(fd, SIOCSIFNETMASK, siocsifnetmask) 48 | # ifconfig tun0 up 49 | ifr2 = struct.pack(flags_mt, dev, 0) 50 | ifr_ret = fcntl.ioctl(fd, SIOCGIFFLAGS, ifr2) 51 | cur_flags = struct.unpack(flags_mt, ifr_ret)[1] 52 | flags = cur_flags | (IFF_UP | IFF_RUNNING) 53 | ifr_ret = struct.pack(flags_mt, dev, flags) 54 | ifr_ret = fcntl.ioctl(fd, SIOCSIFFLAGS, ifr_ret) 55 | return 0 56 | 57 | 58 | def make_tun(): 59 | TUNSETIFF = 0x400454ca 60 | TUNSETOWNER = TUNSETIFF + 2 61 | IFF_TUN = 0x0001 62 | IFF_NO_PI = 0x1000 63 | 64 | # Open TUN device file. 65 | tun = open('/dev/net/tun', 'r+b') 66 | # Tall it we want a TUN device named tun0. 67 | ifr = struct.pack('16sH', 'tun%d', IFF_TUN | IFF_NO_PI) 68 | ret = fcntl.ioctl(tun, TUNSETIFF, ifr) 69 | dev, _ = struct.unpack('16sH', ret) 70 | dev = dev.strip() 71 | # Optionally, we want it be accessed by the normal user. 72 | fcntl.ioctl(tun, TUNSETOWNER, 1000) 73 | return dev, tun 74 | 75 | 76 | def show_buf(buf): 77 | for pos, word in enumerate(buf): 78 | if pos and (pos % 16 == 0): 79 | print '' 80 | print '%02X' % ord(word), 81 | print '' 82 | 83 | 84 | class Transport(object): 85 | pass 86 | 87 | 88 | class Waiter(object): 89 | def __init__(self): 90 | pass 91 | 92 | def serve(self): 93 | pass 94 | 95 | def add(self): 96 | pass 97 | 98 | def remove(self): 99 | pass 100 | 101 | 102 | def add_route(dev, dest, mask, gw): 103 | # sudo strace route add -net 192.168.0.0/24 gw 192.168.10.1 tun0 104 | # ioctl(3, SIOCADDRT, ifr) 105 | # /usr/include/net/route.h 106 | pad = '\x00' * 8 107 | inet_aton = socket.inet_aton 108 | sockaddr_in_fmt = 'hH4s8s' 109 | rtentry_fmt = 'L16s16s16sH38s' 110 | dst = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(dest), pad) 111 | next_gw = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(gw), pad) 112 | netmask = struct.pack(sockaddr_in_fmt, AF_INET, 0, inet_aton(mask), pad) 113 | rt_flags = RTF_UP | RTF_GATEWAY 114 | rtentry = struct.pack(rtentry_fmt, 115 | 0, dst, next_gw, netmask, rt_flags, '\x00' * 38) 116 | sock = socket.socket(AF_INET, socket.SOCK_DGRAM, 0) 117 | fcntl.ioctl(sock.fileno(), SIOCADDRT, rtentry) 118 | return 0 119 | 120 | 121 | class SelectWait(Waiter): 122 | pass 123 | 124 | 125 | class EPollWait(Waiter): 126 | pass 127 | 128 | 129 | def main(): 130 | embed() 131 | dev, tun = make_tun() 132 | tunfd = tun.fileno() 133 | print 'Allocated ', dev 134 | ifconfig(dev, '192.168.10.2', '255.255.255.0') 135 | embed() 136 | while True: 137 | rcv = os.read(tunfd, 65535) 138 | print '***********************************************' 139 | show_buf(rcv) 140 | os.write(tunfd, rcv) 141 | 142 | 143 | if __name__ == '__main__': 144 | main() 145 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | ''' 3 | Created on 2014年12月6日 4 | 5 | @author: Sunday 6 | ''' 7 | 8 | import fcntl # @UnresolvedImport 9 | import struct 10 | from gevent import monkey 11 | monkey.patch_thread() 12 | 13 | 14 | def make_tun(): 15 | TUNSETIFF = 0x400454ca 16 | TUNSETOWNER = TUNSETIFF + 2 17 | IFF_TUN = 0x0001 18 | IFF_NO_PI = 0x1000 19 | 20 | # Open TUN device file. 21 | tun = open('/dev/net/tun', 'r+b') 22 | # Tall it we want a TUN device named tun0. 23 | ifr = struct.pack('16sH', 'tun%d', IFF_TUN | IFF_NO_PI) 24 | fcntl.ioctl(tun, TUNSETIFF, ifr) 25 | # Optionally, we want it be accessed by the normal user. 26 | fcntl.ioctl(tun, TUNSETOWNER, 1000) 27 | print ifr 28 | return tun.fileno() 29 | 30 | 31 | __all__ = [make_tun, ] 32 | -------------------------------------------------------------------------------- /vpn_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | ''' 4 | Created on 2014年12月6日 5 | 6 | @author: Sunday 7 | client: 8 | eth0: 192.168.2.108/24 9 | tun0: 192.168.10.2/24 10 | ioctl a tun device; 11 | set 192.168.0.1/24 to this tun; 12 | connect to heruilong1988.oicp.net 23456 13 | establish connection, large conn with heartbeat, 14 | first, send my ip 192.168.10.2 to server 15 | ''' 16 | import fcntl # @UnresolvedImport 17 | import socket 18 | import select 19 | import os 20 | import logging 21 | import struct 22 | import time 23 | import subprocess 24 | import sys 25 | logger = logging.getLogger('vpn') 26 | logger.addHandler(logging.StreamHandler()) 27 | logger.setLevel(logging.DEBUG) 28 | 29 | 30 | def make_tun(): 31 | TUNSETIFF = 0x400454ca 32 | TUNSETOWNER = TUNSETIFF + 2 33 | IFF_TUN = 0x0001 34 | IFF_NO_PI = 0x1000 35 | 36 | # Open TUN device file. 37 | tun = open('/dev/net/tun', 'r+b') 38 | # Tall it we want a TUN device named tun0. 39 | ifr = struct.pack('16sH', 'tun%d', IFF_TUN | IFF_NO_PI) 40 | fcntl.ioctl(tun, TUNSETIFF, ifr) 41 | # Optionally, we want it be accessed by the normal user. 42 | fcntl.ioctl(tun, TUNSETOWNER, 1000) 43 | return tun 44 | 45 | 46 | def conn_to_vpn(): 47 | sock = socket.socket() 48 | addr = ('heruilong1988.oicp.net', 23456) 49 | try: 50 | sock.connect(addr) 51 | except socket.error as e: 52 | print 'Connect to VPN:[%d],[%s]' % (e.errno, e.strerror) 53 | return None 54 | sock.setblocking(False) 55 | return sock 56 | 57 | 58 | class CliTransportject): 59 | def __init__(self, sock): 60 | self.sock = sock 61 | self.buf = '' 62 | 63 | def set_tunfd(self, tunfd): 64 | self.tunfd = tunfd 65 | 66 | def get_frame(self, buf): 67 | if len(buf) <= 20: 68 | return -1 69 | pack_len = struct.unpack('!H', buf[2:4])[0] 70 | logger.info('FRAME:[%d], BUF:[%d]' % (pack_len, len(buf))) 71 | if len(buf) < pack_len: 72 | return -1 73 | return pack_len 74 | 75 | def recv(self, buf): 76 | self.buf += buf 77 | while True: 78 | # 一次只能写入一个 IP包,帧。 79 | length = self.get_frame(self.buf) 80 | if length == -1: 81 | break 82 | frame = self.buf[:length] 83 | self.buf = self.buf[length:] 84 | os.write(self.tunfd, frame) 85 | logger.info('Write to TUN:[%d]' % len(frame)) 86 | 87 | 88 | def main(): 89 | buflen = 65536 90 | tundev = make_tun() 91 | tunfd = tundev.fileno() 92 | logger.info(u'TUN dev OK, FD:[%d]' % tunfd) 93 | time.sleep(1) 94 | subprocess.check_call('ifconfig tun0 192.168.10.2/24 up', shell=True) 95 | subprocess.check_call('route add -net 192.168.0.0/24 gw 192.168.10.1 tun0', 96 | shell=True) 97 | time.sleep(1) 98 | 99 | sock = conn_to_vpn() 100 | if sock is None: 101 | print u'SOCK dev Fail' 102 | sys.exit(-1) 103 | client = Client(sockTransport client.set_tunfd(tunfd) 104 | sockfd = sock.fileno() 105 | logger.info(u'SOCK dev OK, FD:[%d]' % sockfd) 106 | 107 | fds = [tunfd, sockfd, ] 108 | while True: 109 | rs, _, _ = select.select(fds, [], [], 0.1) 110 | for fd in rs: 111 | if fd == tunfd: 112 | rcv = os.read(tunfd, buflen) 113 | if len(rcv) == 0: 114 | logger.warn(u'TUN recv [0], Continue') 115 | continue 116 | sent_len = sock.send(rcv) 117 | logger.info('TUN recv, write to SOCK:[%r]' % sent_len) 118 | elif fd == sockfd: 119 | rcv = sock.recv(buflen) 120 | if len(rcv) == 0: 121 | logger.warn(u'SOCK recv [0], break') 122 | os.close(sockfd) 123 | break 124 | logger.info('SOCK recv [%d]' % len(rcv)) 125 | client.recv(rcv) 126 | 127 | 128 | if __name__ == '__main__': 129 | main() 130 | --------------------------------------------------------------------------------