设为首页 收藏本站
查看: 895|回复: 0

[经验分享] 用Linux的iptables和Python模拟广域网

[复制链接]

尚未签到

发表于 2015-4-28 05:53:46 | 显示全部楼层 |阅读模式
  更新:哥现在已经发展出了基于TUN/TAP+脚本语言的更成熟、稳定的技术,所以下面的内容算是废了。

  首先声明:本人只是试验成功,还没有进行过严格正确性、性能和稳定性的实验。以下程序可能存在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上服务器看到的效果:
DSC0000.jpg
  很明显服务器看到的是模拟出来的客户ip。
  最后,上代码,再次声明,本人不负任何责任。以下代码进入公共域。

DSC0001.gif DSC0002.gif wansim.py

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)

q = 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这个函数。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-61289-1-1.html 上篇帖子: Python中的字符串对象 下篇帖子: python os模块 常用命令
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表