我阅读本指南编写一个内核模块来做简单的networking过滤。
首先,我不知道下面的文本是什么意思,入站和出站数据包(通过传输层)有什么区别?
当数据包从有线networking进入时,它从物理层,数据链路层,networking层向上传播,因此可能不会通过netfilter中为skb_transport_header定义的function工作。
其次,我讨厌魔术数字,我想用Linux内核的实用程序( 源文件 )中的任何函数replace20
(典型的IP头文件的长度)。
任何帮助将不胜感激。
这篇文章现在有点过时了。 您不理解的文本仅适用于3.11以下的内核版本。
如果您确定您的代码只用于内核> = 3.11,则可以使用下一个代码来处理输入和输出数据包:
udp_header = (struct udphdr *)skb_transport_header(skb);
或者更优雅:
udp_header = udp_hdr(skb);
这是因为传输头已经在ip_rcv()中为你设置了:
skb->transport_header = skb->network_header + iph->ihl*4;
这个改变是由这个提交带来的。
NF_INET_POST_ROUTING
) 在这种情况下.transport_header
字段在sk_buffer
正确设置,所以它指向实际的传输层头(UDP / TCP)。 所以你可以使用这样的代码:
udp_header = (struct udphdr *)skb_transport_header(skb);
或更好看(但实际上是相同的):
udp_header = udp_hdr(skb);
NF_INET_PRE_ROUTING
) 这是棘手的部分。
在这种情况下, .transport_header
字段没有设置为sk_buffer
结构中的实际传输层头(UDP或TCP)(在netfilter钩子函数中)。 而.transport_header
指向IP标头(这是网络层标头)。
所以你需要自己计算传输头的地址。 为此,您需要跳过IP标头(即将IP标头长度添加到您的.transport_header
地址)。 这就是为什么你可以看到文章中的下一个代码:
udp_header = (struct udphdr *)(skb_transport_header(skb) + 20);
所以这里只是IP头的长度。
这样做可以做得更高雅:
struct iphdr *iph; struct udphdr *udph; iph = ip_hdr(skb); /* If transport header is not set for this kernel version */ if (skb_transport_header(skb) == (unsigned char *)iph) udph = (unsigned char *)iph + (iph->ihl * 4); /* skip IP header */ else udph = udp_hdr(skb);
在这个代码中,我们使用了一个实际的IP头大小(这是iph->ihl * 4
,以字节为单位),而不是20
的幻数。
下一个代码中的另一个幻数是17
:
if (ip_header->protocol == 17) {
在这段代码中你应该使用IPPROTO_UDP
而不是17
:
#include <linux/udp.h> if (ip_header->protocol == IPPROTO_UDP) {
如果您需要关于netfilter中传入和传出数据包之间差异的一些参考信息,请参见下图。
细节:
[1]: 来自GitHub的一些有用的代码
[2]: Rami Rosen的“Linux coreel Networking:Implementation and Theory”
[3]: 这个答案可能也有用