Python網(wǎng)絡(luò)編程4--實(shí)現(xiàn)IP分片與網(wǎng)絡(luò)路徑MTU探測(cè)

一、IP分片原理

1.1 基本原理

  • IP包長(zhǎng)計(jì)算
    ??IP 包全長(zhǎng)由頭部中的 total length 字段決定,該字段共 16 位,因此一個(gè) IP 包最大可達(dá) 2^16-1 ,即 65535 字節(jié)。除去頭部 20字節(jié)(無Options字段),IP 包最多可承載65535-20,即 65515 字節(jié)的數(shù)據(jù)。
  • MTU計(jì)算
    ??MTU是最大傳輸單元( Maximum Transmission Unit)的縮寫,指一個(gè)接口無需分片所能發(fā)送的數(shù)據(jù)包的最大字節(jié)數(shù)。
    ??MTU范圍在46 ~ 1500字節(jié),默認(rèn)一般都是1500(7字節(jié)前導(dǎo)碼+1字節(jié)幀開始定界符+6字節(jié)的目的MAC+6字節(jié)的源MAC+2字節(jié)的幀類型+1500字節(jié)IP頭及數(shù)據(jù)+4字節(jié)的 FCS = 1526字節(jié)。抓包軟件抓到的是去掉前導(dǎo)碼、幀開始定界符、FCS之外的數(shù)據(jù),其最大值是 6+6+2+1500=1514)
  • 產(chǎn)生IP分片的原因
    ??IP分片發(fā)生在IP層,不僅源端主機(jī)會(huì)進(jìn)行分片,中間的路由器也有可能分片,因?yàn)椴煌木W(wǎng)絡(luò)的MTU是不一樣的,如果傳輸路徑上的某個(gè)網(wǎng)絡(luò)的MTU比源端網(wǎng)絡(luò)的MTU要小,路由器就可能對(duì)IP數(shù)據(jù)報(bào)再次進(jìn)行分片。而分片數(shù)據(jù)的重組只會(huì)發(fā)生在目的端的IP層。

1.2 與分片相關(guān)的IP頭部字段

IP頭部
  • 標(biāo)識(shí)符( identification ),IP 包的 ID ,全局自增,短時(shí)間內(nèi)不會(huì)重復(fù),可唯一標(biāo)識(shí)一個(gè) IP 包;

  • 標(biāo)志位( flags ),包括兩個(gè)用于控制和識(shí)別分片的標(biāo)志位;

    • DF 標(biāo)志位禁止中間路由對(duì)該包進(jìn)行分片;
    • MF 標(biāo)志位表明該包之后還有其他分片;
  • 偏移量( fragment offset ),表示一個(gè)分片相對(duì)于原始 IP 包開頭的偏移量,以8字節(jié)為單位;

  • 分片大小計(jì)算
    ??假設(shè)主機(jī)①出口 MTU 是 1500 ,它準(zhǔn)備發(fā)一個(gè)長(zhǎng)度為 4000 字節(jié)的 IP包給主機(jī)②。這個(gè)包必須分片

    分片

    ??如上圖,原包長(zhǎng)達(dá) 4000 字節(jié),其中頭部 20 字節(jié),數(shù)據(jù)部分為 3980 字節(jié)。分片包最大長(zhǎng)度為 1500 ,除去頭部的 20 字節(jié),數(shù)據(jù)部分只剩 1480 。這意味著,原包 3980 字節(jié)至少需要分為 3 片。
    ??由于偏移量字段以 8 字節(jié)為單位,因此每個(gè)分片的數(shù)據(jù)長(zhǎng)度必須為 8 的倍數(shù),最后一片除外。由于 1480 剛好可以被 8 整除,因此分片數(shù)據(jù)長(zhǎng)度可以選擇 1480 。

  • 片偏移計(jì)算
    ??第一個(gè)分片,包含原包前 1480 字節(jié)數(shù)據(jù),因此偏移量 offset=0(數(shù)據(jù)的首個(gè)字節(jié)離原始IP頭部距離為0) ;而 MF=1 表示后面還有其他分片。第二個(gè)分片,包含原包緊接著的 1480 字節(jié)數(shù)據(jù),偏移量offset=(1480+1480)/8=185 ;同樣 MF=1 表示后面還有其他分片。最后一個(gè)分片,包含原包最后 1020 字節(jié)數(shù)據(jù),偏移量(1480*2)/8=370 ;而 MF=0 表示它是最后一片了。

  • 分片重組
    ??分片到達(dá)目標(biāo)主機(jī)后,系統(tǒng)根據(jù)包頭中的源地址、目的地址、標(biāo)識(shí)符、偏移量等字段,將它們重組合成原包。
    ??實(shí)際上,系統(tǒng)會(huì)分配一塊內(nèi)存作為重組分片的緩沖區(qū)。一個(gè)分片包首個(gè)分片達(dá)到后,系統(tǒng)將其移入到該緩沖區(qū),等待其他分片達(dá)到:

    分片重組

    后續(xù)分片達(dá)到后,系統(tǒng)先根據(jù)源地址、目的地址和標(biāo)識(shí)符確定它屬于哪個(gè)包;再根據(jù)偏移量確定它屬于原包的哪個(gè)部分;最后將分片數(shù)據(jù)拼接到原包中。

二、實(shí)現(xiàn)IP分片

2.1實(shí)驗(yàn)拓?fù)?/h2>

實(shí)驗(yàn)拓?fù)淙缦?,linux向R2發(fā)送IP分片,并在R2接口上抓包。


IP分片拓?fù)?/div>

2.2 Python手動(dòng)制作分片

  • Python腳本
#!/usr/bin/python3.4
# -*- coding=utf-8 -*-
from kamene.all import *

#嚴(yán)重注意ICMP的校驗(yàn)和是整ICMP頭部和數(shù)據(jù)部分一起計(jì)算的?。?!
#flags="MF"或flags=1表示后面有分片,flags=0表示為最后一個(gè)分片,flags="DF"或flag=2表示不分片
#frag一位表示8字節(jié),因此frag數(shù)值乘以8,才是真正的偏移量字節(jié)數(shù)!第一個(gè)分片需加上ICMP首部8字節(jié)。
send(IP(flags="MF",frag=0,id=1,dst='172.16.10.2')/ICMP(chksum=0x7932)/b'Farst Hello Word!!!!!!!!')
send(IP(flags=1,frag=4,id=1,proto=1,dst='172.16.10.2')/(b'second Hello Word!!!!!!!'))
send(IP(flags=0,frag=7,id=1,proto=1,dst='172.16.10.2')/(b'third Hello Word!!'))
  • 執(zhí)行效果
    執(zhí)行效果如下:


    分片

    抓包結(jié)果如下,發(fā)送三個(gè)數(shù)據(jù)包,前兩包MF位置1.


    分片1

    分片2

    分片3

2.2.1 ICMP校驗(yàn)和計(jì)算

??ICMP包校驗(yàn)和是連通頭部信息加數(shù)據(jù)本身一起進(jìn)行校驗(yàn)(ip包只需要校驗(yàn)頭部信息)而Scapy自動(dòng)添加ICMP校驗(yàn)和時(shí)只會(huì)計(jì)算第一分片的數(shù)據(jù),當(dāng)三個(gè)分片到達(dá)目標(biāo)主機(jī)進(jìn)行重組后校驗(yàn)不通過,將重組后的數(shù)據(jù)包丟棄;因此在手動(dòng)設(shè)置IP分片時(shí),需要手動(dòng)將校驗(yàn)和添加入ICMP首部中。
由于手動(dòng)計(jì)算校驗(yàn)和過程較復(fù)雜,可通過wireshark抓包,可以獲取到正確的校驗(yàn)和。


ICMP校驗(yàn)和

2.3 Scapy自動(dòng)分片

  • python腳本
#!/usr/bin/python3.4
# -*- coding=utf-8 -*-
from kamene.all import *

##############################自動(dòng)制造Fragment################################
frags = fragment(IP(dst='172.16.10.2')/ICMP()/(b"Hello Word"*300))
#產(chǎn)生每一個(gè)分片,可以對(duì)分片就行修改?。。?!
send(frags)

#正常發(fā)包,系統(tǒng)會(huì)自動(dòng)進(jìn)行分片處理!?。?!
#send(IP(dst='172.16.10.2')/ICMP()/(b"Hello Word"*300))

抓包結(jié)果如下,由于單個(gè)數(shù)據(jù)包長(zhǎng)度超過MTU,系統(tǒng)自動(dòng)將ICMP request包分片發(fā)送,同樣的ICMP reply系統(tǒng)也進(jìn)行了分片。


自動(dòng)分片

三、網(wǎng)絡(luò)路徑MTU探測(cè)

3.1 基本原理

??當(dāng)主機(jī)發(fā)送分組的長(zhǎng)度超過MTU又不可以分片(IP flags位DF置1),則這個(gè)分組丟棄,并用ICMP差錯(cuò)報(bào)文向主機(jī)報(bào)告。

3.2 具體實(shí)現(xiàn)

  • 實(shí)驗(yàn)拓?fù)?br> 實(shí)驗(yàn)拓?fù)淙缦拢诼酚善鞒鼋涌谠O(shè)置不同的MTU值。


    MTU路徑
  • Python腳本
#!/usr/bin/python3.4
# -*- coding=utf-8 -*-

from kamene.all import *
import time
import re

def ping_df(dst,mtu):
    pyload = b'v'*(int(mtu) - 28) #28為20字節(jié)IP頭部和8字節(jié)ICMP頭部的長(zhǎng)度

    ping_one_reply = sr1(IP(dst=dst,flags='DF')/ICMP()/pyload, timeout = 1, verbose=False)
    #發(fā)送DF位的數(shù)據(jù)包
    try:
        if ping_one_reply.getlayer(ICMP).type == 3 and ping_one_reply.getlayer(ICMP).code == 4:
            #如果返回ICMP不可達(dá)信息,就返回1和當(dāng)前的mtu
            MTU=ping_one_reply.getlayer(ICMP).unused#獲取經(jīng)過設(shè)備出接口的MTU值
            print("中間設(shè)備出接口MTU值為:", MTU)
            return 1
        elif ping_one_reply.getlayer(ICMP).type == 0 and ping_one_reply.getlayer(ICMP).code == 0:
            #如果返回ICMP echo reply,就返回2和當(dāng)前的mtu
            return 2, mtu
    except Exception as e:
        if re.match('.*NoneType.*',str(e)):
            return None #如果測(cè)試失敗,就返回None
def discover_path_mtu(dst):
    mtu = 1500 #mtu從1500開始向下減
    while True:
        Result = ping_df(dst,mtu)
        if Result == None: #如果測(cè)試失敗就打印信息,并且跳出循環(huán)
            print('目標(biāo): ' + dst + '不可達(dá)!')
            break
        elif Result[0] == 2: #如果PING測(cè)試成功,就打印信息,并且跳出循環(huán)
            print('目標(biāo): ' + dst + '的Path MTU為: ' + str(Result[1]))
            break
        elif Result[0] == 1: #如果得到不可達(dá)信息,就較少M(fèi)TU,打印消息,并且繼續(xù)循環(huán)  
            mtu = mtu - 50
        time.sleep(1)

if __name__ == '__main__':
    dest=input("目標(biāo)IP>>>")
    discover_path_mtu(dest)
  • 執(zhí)行效果
    執(zhí)行結(jié)果如下,返回經(jīng)過的中間設(shè)備出接口的MTU值,如此則可得知路徑中最小的MTU值。


    MTU探測(cè)

    抓包結(jié)果如下:
    request包中Flags DF置1,不允許分片。


    request

    由于分組長(zhǎng)度超過了該設(shè)備出接口的MTU值,故返回一個(gè)ICMP差錯(cuò)報(bào)文告知分組長(zhǎng)度超過MTU,并將下一跳的MTU值返回。
    ICMP 不允許分片

參考:(https://fasionchan.com/network/ip/fragmentation/)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容