├── .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 |
--------------------------------------------------------------------------------