深入Linux:Netfilter包转发流程全解析

目录

一、Netfilter 是什么

二、Netfilter 工作原理

(一)Netfilter 的 5 个 hook 点

(二)hook 函数的机制

(三)数据包处理流程

三、在 Linux 中编写 Netfilter 追踪包转发流程的具体步骤

(一)前期准备工作

(二)代码实现要点

1. 注册 hook 函数

2. 编写 hook 函数

3. 模块的初始化与卸载

四、实际案例分析

(一)案例背景

(二)代码实现

(三)运行与测试

五、总结与展望


一、Netfilter 是什么

        在 Linux 网络系统中,Netfilter 可谓是一个极其关键的存在,它是 Linux 内核的一个子系统,为网络相关操作提供了强大的支持。其采用模块化设计,这种设计方式使得它具有良好的可扩充性,就像搭积木一样,开发者可以根据实际需求轻松添加或修改功能模块。

        Netfilter 与 IP 协议栈无缝契合,如同紧密咬合的齿轮,协同工作。它允许使用者对数据报进行多种操作,其中最常见的就是数据报过滤,通过设定一系列规则,决定哪些数据报可以通过,哪些需要被丢弃,就如同海关检查货物,符合规定的放行,不符合的则被拦截;地址转换也是它的重要功能之一,比如网络地址转换(NAT),可以实现私有网络地址与公共网络地址之间的转换,让多个设备能够共享一个公网 IP 访问互联网;它还能对数据报进行处理,例如修改数据报的某些字段等。

二、Netfilter 工作原理

(一)Netfilter 的 5 个 hook 点

        Netfilter 在 Linux 网络协议栈中设置了 5 个关键的 hook 点,它们就像网络交通要道上的关卡,对数据包的流向和处理起着决定性作用。这 5 个 hook 点分别是 PREROUTING、LOCAL_IN、FORWARD、LOCAL_OUT 和 POSTROUTING 。

  • PREROUTING:当数据包进入系统时,首先会到达 PREROUTING hook 点,这个点位于数据包刚刚进入网络层,还未进行路由查找之前,如同城市入口的检查站,对所有进入的 “访客” 进行初步筛查。在这里,常见的操作是进行目的地址转换(DNAT),比如将外部对服务器公网 IP 的访问请求,转换为对内部实际服务器私有 IP 的访问。

  • LOCAL_IN:若数据包的目的地是本机,在经过 PREROUTING 的初步处理和路由查找后,会进入 LOCAL_IN hook 点,它是通往本机的 “最后一道门岗”。在这个点,主要进行输入数据包的过滤,决定哪些数据包可以被本机接收并处理,哪些需要被丢弃,就像门卫检查访客是否被允许进入特定房间。

  • FORWARD:当数据包不是发往本机,而是需要转发到其他主机时,就会经过 FORWARD hook 点,它承担着数据包 “中转站” 的角色。在这个点,主要对转发的数据包进行过滤,判断这些 “过路客” 是否符合转发规则,以确保网络转发的安全性和合理性。

  • LOCAL_OUT:当本机进程产生数据包要发送出去时,会先到达 LOCAL_OUT hook 点,它是数据包离开本机前的 “内部审核点”。在这里可以对数据包进行源地址转换(SNAT)等处理,比如将本机不同进程发出的数据包的源地址统一转换为公网 IP,以便在公网上进行传输。

  • POSTROUTING:这是数据包离开系统前的最后一个 hook 点,所有即将通过网络设备发送出去的数据包都会经过这里,就像货物离开仓库前的最终安检。在 POSTROUTING 点,通常进行源地址转换等操作,确保数据包以正确的源地址发送到网络中。

        为了更直观地理解这 5 个 hook 点的位置和作用,我们来看下面这张图:

        从图中可以清晰地看到数据包在不同情况下流经各个 hook 点的路径,比如外部数据包进入系统时,先经过 PREROUTING,若目的地为本机则进入 LOCAL_IN,若需转发则进入 FORWARD;本机发出的数据包则先经过 LOCAL_OUT,最后经过 POSTROUTING 离开系统。

(二)hook 函数的机制

        在 Netfilter 的每个 hook 点上,都可以注册相应的 hook 函数,这些函数就像是关卡上的工作人员,负责对经过的数据包进行具体的处理和决策。

  • 注册方式:注册 hook 函数需要使用特定的结构体和函数。在 Linux 内核中,通过定义struct nf_hook_ops结构体来描述 hook 函数的相关信息,包括 hook 函数指针、所属模块、协议族、hook 点编号以及优先级等。例如:

static struct nf_hook_ops nf_test_ops[] __read_mostly = {

{

.hook = nf_test_in_hook,

.owner = THIS_MODULE,

.pf = NFPROTO_IPV4,

.hooknum = NF_INET_LOCAL_IN,

.priority = NF_IP_PRI_FIRST,

},

};

        然后使用nf_register_hooks函数将这些 hook 函数注册到对应的 hook 点上,如nf_register_hooks(nf_test_ops, ARRAY_SIZE(nf_test_ops));。

  • 参数:hook 函数通常接收几个重要参数,以nf_test_in_hook函数为例:
static unsigned int nf_test_in_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff*))
  • hook:表示当前所处的 hook 点编号,通过这个参数可以判断函数在哪个 hook 点被调用,从而执行相应的处理逻辑。

  • skb:即struct sk_buff结构体指针,它是 Linux 内核中用于表示网络数据包的核心数据结构,包含了数据包的各种信息,如数据内容、源地址、目的地址、协议类型等,就像一个装满货物信息的清单,hook 函数可以通过它来获取和修改数据包的相关内容。

  • in和out:分别指向数据包进入和离开的网络设备,通过这两个参数可以了解数据包的来源和去向,比如判断数据包是从哪个网卡进入系统,又要从哪个网卡发送出去。

  • okfn:是一个函数指针,当所有该 hook 点的登记函数调用完后,会调用这个函数来继续后续的处理,它就像是接力赛中的下一棒选手,在当前 hook 函数处理完后接过数据包继续处理流程。

  • 返回值:hook 函数的返回值决定了数据包的后续命运,常见的返回值有以下几种:
  • NF_ACCEPT:表示接受该数据包,允许其继续正常传输到网络协议栈的下一个阶段,就像关卡工作人员放行车辆,让它继续前行。例如在 LOCAL_IN hook 点的 hook 函数中,如果判断数据包符合接收规则,就返回 NF_ACCEPT,让数据包被本机接收处理。

  • NF_DROP:意味着丢弃该数据包,不再继续传输,如同关卡拦截了一辆违规车辆,禁止其通行。比如在 FORWARD hook 点,如果发现某个数据包来源可疑,可能存在安全风险,就可以返回 NF_DROP 将其丢弃,防止潜在威胁传播到其他网络。

  • NF_STOLEN:表示模块接管该数据包,告诉 Netfilter “忘掉” 这个数据包,后续由该回调函数独自处理,Netfilter 不再参与,就像某个特殊部门直接接管了一个重要物资,其他部门不再干涉。例如在某些特殊的网络处理场景中,某个模块需要对特定数据包进行深度处理,就可以返回 NF_STOLEN 接管数据包。

  • NF_QUEUE:用于将数据包进行排队,通常用于将数据包交给用户空间的进程进行处理,就像将一批货物先放在仓库排队,等待后续特定的处理流程。比如一些需要用户空间程序进行详细分析或特殊处理的数据包,可以返回 NF_QUEUE 将其排入队列,由用户空间程序进一步处理。

  • NF_REPEAT:会再次调用该回调函数,但使用时需要谨慎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大雨淅淅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值