netfilter-like内核模块获取源和目标地址

时间:2015-04-15 16:42:05

标签: linux linux-kernel kernel-module

我读this guide编写内核模块来进行简单的网络过滤。

首先,我不知道这意味着什么,以及入站和出站数据包(通过传输层)之间的区别是什么?

  

当数据包从线路进入时,它从物理层传输数据   链路层,网络层向上,因此可能无法通过   在netfilter中为skb_transport_header定义的函数可以工作。

其次,我讨厌幻数,我想用linux内核实用程序(source file)中的任何函数替换20(典型IP头的长度)。 / p>

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:6)

这篇文章现在有点过时了。您不理解的文本仅适用于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;

此更改由this commit带来。

对于旧内核(< 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);

所以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-hooks

详细说明:

[1]:Some useful code from GitHub

[2]:"Linux Kernel Networking: Implementation and Theory" by Rami Rosen

[3]:This answer may be also useful