2017-07-17 40 views
0

我正在通過IP過濾器攔截,修改和重新注入傳出的IPv4 TCP數據包。 問題是,在我改變數據包並設置IP和TCP校驗和後,當我用Wireshark分析結果數據包時,IP校驗和等於0(我正在計算的校驗和似乎是正確的,因爲它等於Wireshark的建議一個)。macOS NKE ipf_filter - IP校驗和爲0

這裏是我下面的步驟,我希望有一個人誰可以當場錯誤或提出處理事情有更好的方式:

static int handle_packet(mbuf_t* data, int ip_len, int dir, ipf_pktopts_t options) 
{ 
    errno_t result = 0; 
    unsigned char packet[1500]; 
    struct tcphdr *tcp; 
    struct ip *ip; 
    mbuf_t old_packet = *data, new_packet; 
    uint32_t mbufs = 0, packet_bytes = 0; 

    // zero packet 
    bzero(packet, sizeof(packet)); 

    // "finalize" the packet so that it is safe to modify it 
    mbuf_outbound_finalize(*data, AF_INET, 0); 

    // get length of mbuf chain 
    do 
    { 
        mbufs++; 
        packet_bytes += mbuf_len(old_packet); 
        old_packet = mbuf_next(old_packet); 
    } while (old_packet != NULL); 

    // copy data to local buffer 
    if (0 != (result = mbuf_copydata(*data, 0, packet_bytes, packet))) { 
        printf("mbuf_copydata returned %d", result); 
        return 0; 
    } 

    // pointer to start IP header 
    ip = (struct ip*)packet; 
    tcp = (struct tcphdr*)((u_int32_t*)ip + ip->ip_hl); 

    // only consider SYN packet 
    if (!(tcp->th_flags & TH_SYN)) 
        return KERN_SUCCESS; 

    if (0 != (result = mbuf_dup(*data, MBUF_DONTWAIT, &new_packet))) 
    { 
        printf("ERROR - mbuf_dup: unable to duplicate mbuf, %d", result); 
        return 0; 
    } 


/** 
… I’m modifying the packet and recalculating ip and tcp’s checksums here 
(by previously setting them to 0, so to avoid that the previous values 
are considered in the calculation) … 
*/ 


    /* 
     * Copy buffer back to mbuf 
     */ 
    if (0 != (result = mbuf_copyback(new_packet, 0, ntohs(ip->ip_len), packet, MBUF_DONTWAIT))) 
    { 
         mbuf_freem(new_packet); 

        switch (result) { 
            case EINVAL: 
                printf("ERROR - handle_packet: mbuf_copyback returned EINVAL"); 
                return 0; 
                break; 
            case ENOBUFS: 
                printf("ERROR - handle_packet: mbuf_copyback returned ENOBUFS"); 
                return 0; 
                break; 
            default: 
                break; 
        } 
    } 

    // recompute any checksums invalidated by data changes 
//    mbuf_outbound_finalize(new_packet, AF_INET, 0); // -> PANIC(m->m_flags & M_PKTHDR) 

  // is this necessary? 
    mbuf_set_csum_performed(new_packet, MBUF_CSUM_DID_IP | MBUF_CSUM_IP_GOOD | MBUF_CSUM_DID_DATA | MBUF_CSUM_PSEUDO_HDR, checksum_ip(ip)); 

    result = ipf_inject_output(new_packet, ip_filter_ref, options); 

    return result == 0 ? EJUSTRETURN : result; 
} 

static errno_t ip_filter_output(void* cookie, mbuf_t *data, ipf_pktopts_t options) 
{ 
    struct ip *ip; 
    char src[32], dst[32]; 
    int ip_len; 

    // pointer to start IP header 
    ip = (struct ip*)mbuf_data(*data); 
    ip_len = ntohs(ip->ip_len); 

    bzero(src, sizeof(src)); 
    bzero(dst, sizeof(dst)); 

    // converts the network address structure into a character string 
    inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src)); 
    inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst)); 

    // avoid congestion and filter only packets from/to tcpcrypt website 
    if (ip->ip_p == IPPROTO_TCP 
        && mbuf_flags(*data) == MBUF_PKTHDR) { 
        return handle_packet(data, ip_len, DIRECTION_OUT /* 1 */, options); 
    } 

    // continue with normal processing of the packet 
    return KERN_SUCCESS; 
} 

我懷疑越來越零是重新計算的預期後果整個頭部的校驗和,即它導致計算出的總和自行消失。

無論如何,我真的不明白爲什麼會發生這種情況。

有沒有人知道答案或可以幫助?

非常感謝你提前,

羅密歐

回答

0

您看到非零校驗,如果你不運行您的程序?

如果沒有,那麼您的網卡可能會執行校驗和卸載,這使得驅動程序實際上不會在數據包中插入任何校驗和(因爲網絡接口將負責它)。

請參閱WireShark文檔的section 7.10.2. (Checksum offloading)

+0

他正在調用'mbuf_outbound_finalize()',請參閱該方法的文檔。不管你的硬件是否卸載,都調用這個函數來保證正確的校驗和。從文檔:「*有許多操作是在硬件中執行的,例如計算校驗和。這個函數將在軟件中執行計劃在硬件中完成的各種操作。*」所以你的回答沒有意義,我不明白爲什麼這是被接受的答案。 – Mecki

1

首先,這

do 
{ 
    mbufs++; 
    packet_bytes += mbuf_len(old_packet); 
    old_packet = mbuf_next(old_packet); 
} while (old_packet != NULL); 

是得到一個的mbuf鏈的長度的可怕的方式。正確的方法是

size_t totalLength = (
    mbuf_flags(mbuf) & MBUF_PKTHDR ? 
    mbuf_pkthdr_len(mbuf) : mbuf_len(mbuf) 
); 

由於要麼是的mbuf的鏈,但那時,鏈的第一mbuf中應該具有的一攬子貸款報頭和這個一攬子貸款頭包含整個MBUF鏈的大小(α鏈無這樣的頭文件根據定義而被破壞),或者它只是一個單獨的mbuf,但是隻需要向這個mbuf請求它的大小就足夠了。在整個內核代碼中,我的代碼如上所示檢索了一個mbuf鏈的大小。

那麼請您瞭解mbuf_outbound_finalize()的功能。從該方法的文檔:

此函數將「最終確定」數據包,允許您的代碼檢查最終數據包。

在硬件中執行一些操作,例如計算校驗和。該功能將在軟件中執行計劃在硬件中完成的各種操作。

因此,如果校驗和計算被卸載到硬件或沒有,當你調用該函數時,數據包應該有正確的校驗和。如果您然後修改數據包,則需要您再次修復校驗和。您可以通過重新計算校驗和來完成此操作,一旦完成修改,或「修復」現有的校驗和(例如,如果您知道哪些舊數據轉化爲哪些新數據,則可以「修復」舊的校驗和以匹配新數據,而不用計算從頭開始的所有數據,這有點棘手,但會使計算速度更快)。

但所有這一切不能僅通過在年底再次調用mbuf_outbound_finalize()完成。你可以在mbuf或mbuf鏈上只調用一次這個函數,然後它取決於你,這意味着你的代碼保持校驗和正確。 mbuf_outbound_finalize()只有在校驗和計算尚未完成且已計劃在硬件中完成時纔會執行某些操作。如果你的硬件不支持卸載,那麼一旦你的過濾器捕獲了數據包,數據包就會有一個正確的校驗和,然後mbuf_outbound_finalize()將不會做任何事情,因爲它沒有什麼可做的。

最後一個問題:你是不是應該叫mbuf_set_csum_performed()。請參閱該功能的文檔:

驅動程序使用此函數向堆棧指示哪些校驗和操作是在硬件中執行的。

「的司機」,你是不是司機的人;儘管此功能用於傳入數據包而不用於傳出數據包。