用Linux的iptables和Python類比廣域網路

來源:互聯網
上載者:User

更新:哥現在已經發展出了基於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上伺服器看到的效果:

 

很明顯伺服器看到的是類比出來的客戶ip。

 最後,上代碼,再次聲明,本人不負任何責任。以下代碼進入公用域。

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這個函數。 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.