2017-07-17 114 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()。请参阅该功能的文档:

驱动程序使用此函数向堆栈指示哪些校验和操作是在硬件中执行的。

“的司机”,你是不是司机的人;尽管此功能用于传入数据包而不用于传出数据包。