类似netfilter的内核模块获取源地址和目的地址

我阅读本指南编写一个内核模块来做简单的networking过滤。

首先,我不知道下面的文本是什么意思,入站和出站数据包(通过传输层)有什么区别?

当数据包从有线networking进入时,它从物理层,数据链路层,networking层向上传播,因此可能不会通过netfilter中为skb_transport_header定义的function工作。

其次,我讨厌魔术数字,我想用Linux内核的实用程序( 源文件 )中的任何函数replace20 (典型的IP头文件的长度)。

任何帮助将不胜感激。

这篇文章现在有点过时了。 您不理解的文本仅适用于3.11以下的内核版本。

对于新内核(> = 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; 

这个改变是由这个提交带来的。

对于旧的内核(<3.11)

传出数据包( 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输入/输出包的解释

如果您需要关于netfilter中传入和传出数据包之间差异的一些参考信息,请参见下图。

netfilter的钩

细节:

[1]: 来自GitHub的一些有用的代码

[2]: Rami Rosen的“Linux coreel Networking:Implementation and Theory”

[3]: 这个答案可能也有用