在一台PC上模拟多个IP地址向服务器发起socket连接,并在建立连接后发数据包

http://www.cnblogs.com/yushih/archive/2010/06/01/1749378.html

 

用Linux的iptables和Python模拟广域网

首先声明:本人只是试验成功,还没有进行过严格正确性、性能和稳定性的实验。以下程序可能存在bug。使用者自己注意。

问题 :有人在豆瓣Python版提出“在一台PC上模拟多个IP地址向服务器发起socket连接,并在建立连接后发数据包“的需求。具体要求是“可以在 界面上输入想要模拟的IP地址的范围,比如我想模拟100个用户,就输入192.168.4.100——192.168.4.200,然后自动以这些IP 地址向服务器发socket连接和一些数据“,“服务器侧是一个注册系统,以IP和***区分不同用户,我要想进行压力测试必须模拟不同的IP地址“。环 境是“测试所有使用的都是私网地址,而且环境比较独立,所以不必考虑NAT和IP冲突问题“。限制:”服务器侧要是能动就好了,就是不能改动“。以下是我 的解决方法。

搭建环境 :服务器端环境不限。客户端必须是Linux。服务器和客户直接通过LAN连接。以上只是逻辑要求,可以用虚拟机在一台电脑上实现。客户端ip地 址设置为cip,掩码nm,服务器端ip地址设置为sip(sip&nm == cip&nm),掩码nm,缺省网关cip(注意,是把服务器的缺省网关地址设置为客户ip地址。课后练习:为什么?)。

软件安装 :在客户端装nfqueue-bindings(http://software.inl.fr/trac/wiki/nfqueue- bindings)。有大量的依赖需要安装,不过不需要Perl,只要把CMakeLists.txt中“ADD_SUBDIRECTORY(perl) “这一行去掉。另外libnetfilter-queue要自己下载编译安装,至少在我的机器上用rpm安装后nfqueue-bindings不认。

运行ip模拟器 :在客户端以root权限运行wansim.py(代码在后面),命令行参数为:
python wansim.py sip cip ip_range
其中sip为上面设置的服务器ip,cip为上面设置的客户端机器ip,ip_range为要模拟的ip范围,例如:
1.1.1.1-254

1.1.1-254.1-254

222-235.1-8.2.3-6 

限制:模拟ip不能进入客户端-服务器所用LAN的子网ip范围,即对任何模拟ip地址fip:fip&nm != sip&nm(课后作业:为什么?)。
ip模拟器开始运行后,所有从客户端发起到服务器端的tcp会话将被ip模拟器从指定ip范围中随机分配一个ip。服务器程序看到的是这个模拟的ip,而不是cip。

运行:运行服务器。在客户机上运行客户端测试软件。以下是我Windows上服务器看到的效果:

在一台PC上模拟多个IP地址向服务器发起socket连接,并在建立连接后发数据包  

很明显服务器看到的是模拟出来的客户ip。

 最后,上代码,再次声明,本人不负任何责任。以下代码进入公共域。

在一台PC上模拟多个IP地址向服务器发起socket连接,并在建立连接后发数据包在一台PC上模拟多个IP地址向服务器发起socket连接,并在建立连接后发数据包wansim.py
<!-- <br/ /> <br/ /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ /> http://www.CodeHighlighter.com/<br/ /> <br/ /> -->import  sys
sys.path.append(r ' /usr/local/lib/python2.6/site-packages ' )
import  nfqueue
import  time
from  socket  import  AF_INET, AF_INET6, inet_ntoa, inet_aton
from  dpkt  import  ip, tcp, hexdump
import  re
import  sys
import  random




svr_ip = sys.argv[ 1 ]

my_ip = sys.argv[ 2 ]


def  parse():
        
for  r  in  sys.argv[ 3 ].split( ' . ' ):
                
if   ' - '   in  r:  # xxx-yyy
                        m = re.match(r ' (\d+)-(\d+) ' , r)
                        
yield  range(int(m.group( 1 )), int(m.group( 2 )) + 1 )
                
else # xxx
                         yield  range(int(r), int(r) + 1 )
fake_ip_range = list(parse())
def  get_fake_ip():
        ip  =   ' . ' .join(str(random.choice(r))  for  r  in  fake_ip_range)
        
print   ' fake ip:  ' , ip
        
return  ip
        
# Track connection by src port. This is very crude. Should watch tcp more closely.
port2ip = {}

time0  =  None

def  cb(i,payload):
        
global  time0
        data  =  payload.get_data()
        pkt  =  ip.IP(data)
        
if   not  time0:
                time0 = time.time()
        
print  time.time() - time0,\
              
" source: %s "   %  inet_ntoa(pkt.src),\
              
" dest: %s "   %  inet_ntoa(pkt.dst)
        
if  pkt.p  ==  ip.IP_PROTO_TCP:
                
# outgoing to target server
                 if  inet_ntoa(pkt.dst)  ==  svr_ip:
                        
if  pkt.tcp.sport  in  port2ip:
                                pkt.src  =  port2ip[pkt.tcp.sport]
                        
else :
                                fip  =  inet_aton(get_fake_ip())
                                port2ip[pkt.tcp.sport] = fip
                                pkt.src  =  fip

                
# incoming from target server
                 elif  inet_ntoa(pkt.src)  ==  svr_ip  and  \
                     inet_ntoa(pkt.dst)  !=  my_ip:
                        
# a stronger condition is to check if it is one of our fake ip
                        pkt.dst  =  inet_aton(my_ip)
                        
                pkt.tcp.sum  =  0
                pkt.sum  =  0
                payload.set_verdict_modified(
                        nfqueue.NF_ACCEPT,str(pkt),len(pkt))

                
return  0
        payload.set_verdict(nfqueue.NF_ACCEPT)

        sys.stdout.flush()
        
return   1

def  run(cmd):
        
from  commands  import  getstatusoutput
        (s, o)  =  getstatusoutput(cmd)
        
print  cmd
        
print   ' exit code  ' , s
        
if  len(o):
                
print  o

cmds  =  [
        
# intercept outgoing packets
         " iptables -t mangle -A POSTROUTING -p tcp -d %s -j NFQUEUE "   %  svr_ip,
        
# intercept incoming packets
         " iptables -t mangle -A PREROUTING  -p tcp -s %s -j NFQUEUE "   %  svr_ip,
        
# "iptables -A INPUT -p tcp -i eth1 -j NFQUEUE "
]

for  c  in  cmds:
        run(c)

=  nfqueue.queue()

print   " open "
q.open()

print   " bind "
q.bind(AF_INET);

print   " setting callback "
q.set_callback(cb)

print   " creating queue "
q.create_queue(0)

print   " trying to run "
try :
        q.try_run()
except  KeyboardInterrupt, e:
        
print   " interrupted "


print   " unbind "
q.unbind(AF_INET)

print   " close "
q.close()

for  c  in  cmds:
        run(c.replace( ' -A ' ' -D ' ))

 

如果需要更改模拟ip分配的方式,只需修改get_fake_ip这个函数。