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


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

    问题

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

    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种,不同的返回值会影响协议栈对报文的后续处理逻辑。
  • 相关阅读:
    观察者模式用途总结
    C语言 利用选择排序法对数组中10个整数由小到大排序
    掌握这3点,企业就能规避收款业务中的合规风险
    【C语言】C语言-学生成绩管理系统(源码+数据文件+课程论文)【独一无二】
    C#异步编程看这篇就够了
    数据加密和BCrypt哈希算法应用 | StartDT Tech Lab 15
    layui中checkbox使用lay-skin=“switch“ 过滤事件赋值与取值
    Dextran-ALK,葡聚糖-炔基|炔基修饰葡聚糖,葡萄糖/乳糖基/黄原胶/岩藻多糖/木聚糖
    python经典百题之求奇数个数
    Opencv中的GrabCut图像分割
  • 原文地址:https://blog.csdn.net/dillanzhou/article/details/126219082