netfilter挂钩没有检索完整的数据包

时间:2019-10-22 08:55:13

标签: linux linux-kernel netfilter

我正在编写一个netfilter模块,该模块可以深入检查数据包。但是,在测试期间,我发现netfilter模块未完全接收到数据包。

为验证这一点,我编写了以下代码来转储在端口80上检索到的数据包,并将结果写入dmesg缓冲区:

const struct iphdr *ip_header = ip_hdr(skb);
if (ip_header->protocol == IPPROTO_TCP)
{
    const struct tcphdr *tcp_header = tcp_hdr(skb);
    if (ntohs(tcp_header->dest) != 80)
    {
        return NF_ACCEPT;
    }

    buff = (char *)kzalloc(skb->len * 10, GFP_KERNEL);
    if (buff != NULL)
    {
        int pos = 0, i = 0;
        for (i = 0; i < skb->len; i ++)
        {
            pos += sprintf(buff + pos, "%02X", skb->data[i] & 0xFF);
        }

        pr_info("(%pI4):%d --> (%pI4):%d, len=%d, data=%s\n",
            &ip_header->saddr,
            ntohs(tcp_header->source),
            &ip_header->daddr,
            ntohs(tcp_header->dest),
            skb->len,
            buff
        );
        kfree (buff);
    }
}

在本地运行的虚拟机中,我可以检索完整的HTTP请求;在阿里云和其他基于OpenStack的VPS提供程序上,数据包在中间被剪切。

为验证这一点,我在另一个VPS上执行curl http://VPS_IP,并在dmesg缓冲区中获得以下输出:

[ 1163.370483] (XXXX):5007 --> (XXXX):80, len=237, data=451600ED000040003106E3983D87A950AC11D273138F00505A468086B44CE19E80180804269300000101080A1D07500A000D2D90474554202F20485454502F312E310D0A486F73743A2033392E3130372E32342E37370D0A4163636570743A202A2F2A0D0A557365722D4167656E743A204D012000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001E798090F5FFFF8C0000007B00000000E0678090F5FFFF823000003E00000040AE798090F5FFFF8C0000003E000000000000000000000000000000000000000000000000000000000000

解码后的结果是这样的

enter image description here

这很奇怪,User-Agent: M之后的所有内容都是“消失”或归零。尽管skb-> len是237,但是丢失了一半的数据包。

有什么想法吗?尝试了PRE_ROUTING和LOCAL_IN,没有更改。

1 个答案:

答案 0 :(得分:1)

看来,有时您收到线性skb,有时您的skb不是线性的。在后一种情况下,您不会读取skb的全部数据内容。

如果skb->data_len为零,则您的skb是线性的,并且skb的完整数据内容在skb->data中。如果skb->data_len不为零,则您的skb不是线性的,并且skb->data仅包含数据的第一(线性)部分。该区域的长度为skb->len - skb->data_lenskb_headlen()辅助函数为方便起见进行了计算。 skb_is_nonlinear()辅助函数告诉skb是否为线性。

其余数据可以按此顺序放在分页片段和skb片段中。

skb_shinfo(skb)->nr_frags告诉分页的片段数。每个分页的片段由结构skb_shinfo(skb)->frags[0..skb_shinfo(skb)->nr_frags]数组中的数据结构描述。 skb_frag_size()skb_frag_address()辅助函数有助于处理这些数据。它们接受描述分页片段的结构的地址。还有其他有用的帮助程序功能,具体取决于您的内核版本。

如果分页片段中的数据总大小小于skb->data_len,则其余数据位于skb片段中。这是附加到skb_shinfo(skb)->frag_list上的skb的skb列表(请参阅内核中的skb_walk_frags())。

请注意,线性部分可能没有数据,并且/或者分页的片段中没有数据。您只需要按照上述顺序逐个处理数据即可。