netfilter&iptables探讨(2)——netfilter原理与实现

在上一篇文章《netfilter&iptables探讨(1)——基本原理》中,我们分析了netfilter和iptables机制的基本原理以及相关的模块和接口。本文将进一步探讨这套机制的基石——netfilter的原理和实现。

问题

  1. netfilter支持了多少个hook点?分别在内核协议栈的什么位置?
  2. 基于netfilter,可以对协议栈行为做哪些维度的修改?
  3. hook函数的参数和返回值是如何定义的,有什么意义?

netfilter的hook位置

上图来自wiki,是linux内核的报文处理全流程图,其中展示了当前内核支持了所有netfilter hook点。

首先需要明确的是,netfilter并不是专为支持iptables而产生的机制,而是Linux内核网络协议栈的公共机制。因此netfilter中内置的hook点其实相当多,在各类网络报文的处理逻辑中都有相对应的hook点,例如IPv4、IPv6、ARP、bridge等。iptables中所使用的是IPv4报文处理路径中的5个hook点,hook点在iptables中被称为链(chain),这可能是因为在每个hook点上都会用链表的形式维护多种iptables规则表的不同处理函数。IPv6的处理路径中也有5个hook点,功能逻辑上和IPv4的是一致的。

下面对netfilter hook点的介绍和图片很大部分来自于《来,今天飞哥带你理解 iptables 原理!》这篇文章,关于IPv4 hook点的更多细节可以在这篇文章中找到。

IPv4IPv6
PRE_ROUTINGip_rcvipv6_rcv
LOCAL_INip_local_deliverip6_input
FORWARDip_forwardip6_forward
LOCAL_OUT__ip_local_out__ip6_local_out/ip6_xmit
POST_ROUTINGip_outputip6_output

上面的表格中列出了IPv4、IPv6协议栈中的5个hook点名称和位置。

  1. PRE_ROUTING:收到外部发来的报文,在ip_rcv/ipv6_rcv函数中,执行本地路由选择前执行
  2. LOCAL_IN:根据收到的报文的目的地址,判断报文是发送给本设备后,在本地报文处理函数ip_local_deliver/ip6_input中执行
  3. FORWARD:根据收到的报文的目的地址,判断报文不是发送给本设备,而是需要转发给其他设备,在转发函数ip_forward/ip6_forward中执行
  4. LOCAL_OUT:在本地发送报文的处理过程中,完成路由选择后,在__ip_local_out/__ip6_local_out中执行
  5. POST_ROUTING:在报文构造和路由选择完成后,在ip_output/ip6_output函数中执行。在本地发送报文的执行路径中,LOCAL_OUT和POST_ROUTING几乎是连续执行的。之所以要分别定义两处hook,是因为最终需要发送报文的不止是本地发送的场景,还有转发的场景,因此POST_ROUTING和LOCAL_OUT被执行到的情况并不是完全相同的。

在本地接收报文的过程中,会执行PRE_ROUTING和LOCAL_IN位置的hook函数。如下图所示:

 在本地发送报文的过程中,会执行LOCAL_OUT和POST_ROUTING位置的hook函数。如下图所示:

 在收到需要转发的报文的处理过程中,会执行PRE_ROUTING、FORWARD和POST_ROUTING位置的hook函数。如下图所示:

 把以上这些路径结合起来,就是IP协议栈对报文的主要处理逻辑:

hook函数

netfilter中注册的hook函数指针定义为:

typedef unsigned int nf_hookfn(void *priv,
                   struct sk_buff *skb,
                   const struct nf_hook_state *state);

其中的参数priv是注册hook时提供的私有结构指针。skb是当前处理的数据报文。state则是一些网络上下文信息,包括报文的输入输出网卡、所属的netns、所属的socket信息等。

在hook函数中可以根据网络上下文以及当前的报文内容,对报文进行修改、丢弃、统计、改变后续执行流等各种操作。由于netfilter的使用者都是其他内核模块,本身就有最高权限,因此基于在netfilter hook中可以实现的功能几乎是没有任何限制的。

hook函数的返回值会影响协议栈对报文的后续处理方式,可能的返回值有:

NF_ACCEPT:当前hook函数执行完成,继续执行链表上的下一个hook函数。如果没有下一个hook函数则当前hook点执行完成,返回继续执行协议栈处理流程;

NF_DROP:报文skb需要丢弃,释放掉skb并结束处理流程;

NF_QUEUE:将报文skb加入队列。这里的队列处理逻辑也是可以定制和注册的,但目前只有nfnetlink_queue模块实现了这个接口。通过nfnetlink_queue提供的queue机制,可以将skb加入到一个队列中,并通过netlink协议接口发送给用户态的程序做进一步处理。

NF_STOLEN:报文skb已被hook函数处理完成或接管,直接结束处理流程;

NF_REPEAT:一些文章中说这个返回值会让hook函数被再次调用,在较新的内核上显然并非如此。从手头的5.9.11内核实现来看,hook函数返回NF_ACCEPT、NF_DROP、NF_QUEUE之外的值都会被当做NF_STOLEN处理。NF_REPEAT只会在一些模块的特定逻辑中使用,例如nfnetlink_queue的报文注入逻辑中。这个值不再能作为hook函数返回值使用。

小结

本文在前一篇文章的基础上,进一步分析了netfilter的原理和具体实现。

  1. netfilter在不同协议的处理流程中提供了不同的hook点,在IPv4和IPv6协议栈中分别支持了5个hook点。
  2. 在netfilter的hook函数中,可以根据网络上下文以及当前的报文内容,对报文进行修改、丢弃、统计、改变后续执行流等各种操作,作为内核的一部分,hook函数的功能几乎是无限的,因此必须慎重设计和实现。
  3. hook函数的参数包括hook自定义的私有指针、skb和网络上下文信息。hook函数的返回值有4种,不同的返回值会影响协议栈对报文的后续处理逻辑。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值