既然有了一个OpenVPN的前端控制程序,那么就可以在一台机器上启动多个OpenVPN实例了,不同的客户端只要连接不同的实例就好。事实证明,这种方式可以完美利用多处理器的优势,比在OpenVPN内部实现的多进程或者多线程等多处理方式要好很多,这也是广义的KISS原则,也遵循了“每次做好一件事”的原则。
现在的关键问题是如何将这多个OpenVPN实例集合在一起作为一个整体。既然OpenVPN使用了更底层的虚拟网卡技术,那么做这件事的技术也和这些底层技术有关了,我试了下bridge方式,非常不错,bridge在Linux上的实现非常棒,工具也很齐全,不过一个brctl够了。方法是,将多个OpenVPN服务端实例的tap网卡bridge在一起,这样它们就可以共享一个IP地址了,也就可以使用同一个地址段了,剩下的事情,由bridge来完成吧!
我的bridge-multiVPN实现使用“预生成实例”的方式,也就是预先生成一组OpenVPN服务端实例,生成多少由CPU的个数决定,一般生成“CPU*2-2”个即可,我还是相信“操作系统的调度要比OpenVPN本身的调度做得好”...当然也可以使用“每客户端一例”的方式,不过那样对于管理不是很方便,具体也没有测试。
现在的关键问题是如何将这多个OpenVPN实例集合在一起作为一个整体。既然OpenVPN使用了更底层的虚拟网卡技术,那么做这件事的技术也和这些底层技术有关了,我试了下bridge方式,非常不错,bridge在Linux上的实现非常棒,工具也很齐全,不过一个brctl够了。方法是,将多个OpenVPN服务端实例的tap网卡bridge在一起,这样它们就可以共享一个IP地址了,也就可以使用同一个地址段了,剩下的事情,由bridge来完成吧!
我的bridge-multiVPN实现使用“预生成实例”的方式,也就是预先生成一组OpenVPN服务端实例,生成多少由CPU的个数决定,一般生成“CPU*2-2”个即可,我还是相信“操作系统的调度要比OpenVPN本身的调度做得好”...当然也可以使用“每客户端一例”的方式,不过那样对于管理不是很方便,具体也没有测试。
#!/usr/bin/python #coding=gbk import signal import socket import sys import os def signal_handler(signal, frame): os.popen("goaway.sh") sys.exit(0) if __name__ == '__main__': signal.signal(signal.SIGINT, signal_handler) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('0.0.0.0', 61195)) sock.listen(1024) # 起始端口 minport = 6119 # 结束端口 maxport = 6125 # 起始虚拟IP地址分量 minsubnet = 2 delta = 8 currport = minport # 启动(maxport-minport)个OpenVPN实例,侦听不同的端口,分配不同的地址段 while currport <= maxport: if currport == maxport: cmd = "openvpn --config"\ " server.conf"\ " --up createBR.sh"\ " --port %s"\ " --ifconfig-pool 10.8.0.%s 10.8.0.%s 255.255.255.0"\ % (currport, minsubnet, minsubnet+delta) else: cmd = "/root/openvpn-ipv6/openvpn --config"\ " /root/openvpn-ipv6/server.conf"\ " --port %s"\ " --ifconfig-pool 10.8.0.%s 10.8.0.%s 255.255.255.0"\ % (currport, minsubnet, minsubnet+delta) os.popen(cmd) print cmd currport = currport + 1 minsubnet = minsubnet + delta + 1 currport = minport while True: connection,address = sock.accept() try: connection.settimeout(5) buf = connection.recv(1024) if buf == 'getport': # 按照轮转的方式为客户端分配OpenVPN实例 connection.send(str(currport)) currport = currport + 1 if currport > maxport: currport = minport except socket.timeout: os.popen("goaway.sh") print 'time out' connection.close() os.popen("goaway.sh")
最后一个OpenVPN的虚拟网卡启动的时候,调用createBR.sh,将所有的虚拟网卡和bridge联系起来:
#!/bin/bash # 获取服务端的IP地址,实际只需要第一个,因为大家最终都一样 virtual_GW=$(ifconfig tap0|awk -F 'inet addr:' '/inet addr/{print $2}'|awk -F ' ' '{print $1}'); # 创建一个bridge brctl addbr virtual_BR; # 将各个虚拟网卡陆续添加到bridge for virtual_interface in $(ifconfig |grep -o tap[0-9]); do brctl addif virtual_BR $virtual_interface; # 清除已经有的地址 ifconfig $virtual_interface 0.0.0.0; done; # 将地址赋予bridge ifconfig virtual_BR $virtual_GW up
类似的,清理程序goaway.sh:
killall -9 openvpn ifconfig virtual_BR down brctl delbr virtual_BR
服务端的配置文件:
daemon VPNS
script-security 2
mode server
tls-server
tls-cipher RC4-MD5
cipher BF-CBC
duplicate-cn
dev tap
proto udp
keepalive 10 60
# 配置本地的虚拟地址
ifconfig 10.8.0.1 255.255.255.0
local 0.0.0.0
# 留给server.py去设置,多个实例设置成不同的
;port 6119
verb 4
dh dh1024.pem.dh.confca CA.cer
cert A.pem
key A.key
客户端的前端程序:
#!/usr/bin/python #coding=gbk if __name__ == '__main__': import socket import os sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('192.168.2.249', 61195)) # 请求连接端口 sock.send('getport') # 接收连接端口 port = sock.recv(1024) sock.close() # 根据服务器的建议,启动OpenVPN客户端实例 cmd = 'openvpn --config client.conf --remote 192.168.2.249 %s ' % (port) os.popen(cmd)