我正在编写一个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
解码后的结果是这样的
这很奇怪,User-Agent: M
之后的所有内容都是“消失”或归零。尽管skb-> len是237,但是丢失了一半的数据包。
有什么想法吗?尝试了PRE_ROUTING和LOCAL_IN,没有更改。
答案 0 :(得分:1)
看来,有时您收到线性skb,有时您的skb不是线性的。在后一种情况下,您不会读取skb的全部数据内容。
如果skb->data_len
为零,则您的skb是线性的,并且skb的完整数据内容在skb->data
中。如果skb->data_len
不为零,则您的skb不是线性的,并且skb->data
仅包含数据的第一(线性)部分。该区域的长度为skb->len - skb->data_len
。 skb_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()
)。
请注意,线性部分可能没有数据,并且/或者分页的片段中没有数据。您只需要按照上述顺序逐个处理数据即可。