文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

如何实现Netfilter

2024-04-02 19:55

关注

本篇内容介绍了“如何实现Netfilter”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、Netfilter 挂载点

我们先来回顾一下 Netfilter 的原理,Netfilter 是通过在网络协议栈的不同阶段注册钩子函数来实现对数据包的处理与过滤,如 图1  所示:

如何实现Netfilter

(图1 Netfilter挂载点)

在 图1 中,蓝色部分就是 Netfilter 挂载钩子函数的位置,所以 Netfilter 定义了 5 个常量来表示这 5 个位置,如下代码:

// 文件:include/linux/netfilter_ipv4.h  #define NF_IP_PRE_ROUTING   0 #define NF_IP_LOCAL_IN      1 #define NF_IP_FORWARD       2 #define NF_IP_LOCAL_OUT     3 #define NF_IP_POST_ROUTING  4

上面代码中的常量与 图1 中挂载钩子函数的位置一一对应,如常量 NF_IP_PRE_ROUTING 对应着 图1 的 PRE_ROUTING 处。

二、Netfilter 钩子函数链

前面说过,Netfilter 是通过在网络协议中的不同位置挂载钩子函数来对数据包进行过滤和处理,而且每个挂载点能够挂载多个钩子函数,所以  Netfilter 使用链表结构来存储这些钩子函数,如 图2 所示:

如何实现Netfilter

(图2 Netfilter钩子函数链)

如 图2 所示,Netfilter 的每个挂载点都使用一个链表来存储钩子函数列表。在内核中,定义了一个名为 nf_hooks  的数组来存储这些链表,如下代码:

// 文件:net/core/netfilter.c  struct list_head nf_hooks[32][5];

struct list_head 结构是内核的通用链表结构。

从 nf_hooks 变量定义为一个二维数组,第一维是用来表示不同的协议(如 IPv4 或者 IPv6,本文只讨论 IPv4,所以可以把 nf_hooks  当成是一维数组),而第二维用于表示不同的挂载点,如 图2 中的 5 个挂载点。

三、钩子函数

接下来我们介绍一下钩子函数在 Netfilter 中的存储方式。

前面我们介绍过,Netfilter 通过链表来存储钩子函数,而钩子函数是通过结构 nf_hook_ops 来描述的,其定义如下:

// 文件:include/linux/netfilter.h  struct nf_hook_ops {     struct list_head list; // 连接相同挂载点的钩子函数     nf_hookfn *hook;       // 钩子函数指针     int pf;                // 协议类型     int hooknum;           // 钩子函数所在链     int priority;          // 优先级 };

下面我们对 nf_hook_ops 结构的各个字段进行说明:

其中 hook 字段的类型为 nf_hookfn,nf_hookfn 类型的定义如下:

// 文件:include/linux/netfilter.h  typedef unsigned int nf_hookfn(unsigned int hooknum,                                struct sk_buff **skb,                                const struct net_device *in,                                const struct net_device *out,                                int (*okfn)(struct sk_buff *));

我们也介绍一下 nf_hookfn 函数的各个参数的作用:

四、注册钩子函数

当定义好一个钩子函数结构后,需要调用 nf_register_hook 函数来将其注册到 nf_hooks 数组中,nf_register_hook  函数的实现如下:

// 文件:net/core/netfilter.c  int nf_register_hook(struct nf_hook_ops *reg) {     struct list_head *i;      br_write_lock_bh(BR_NETPROTO_LOCK); // 对 nf_hooks 进行上锁      // priority 字段表示钩子函数的优先级     // 所以通过 priority 字段来找到钩子函数的合适位置     for (i = nf_hooks[reg->pf][reg->hooknum].next;          i != &nf_hooks[reg->pf][reg->hooknum];          i = i->next)     {         if (reg->priority < ((struct nf_hook_ops *)i)->priority)             break;     }      list_add(&reg->list, i->prev); // 把钩子函数添加到链表中      br_write_unlock_bh(BR_NETPROTO_LOCK); // 对 nf_hooks 进行解锁      return 0; }

nf_register_hook 函数的实现比较简单,步骤如下:

插入过程如 图3 所示:

如何实现Netfilter

(图3 钩子函数插入过程)

如 图3 所示,我们要把优先级为 20 的钩子函数插入到 PRE_ROUTING 这个链中,而 PRE_ROUTING  链已经存在两个钩子函数,一个优先级为 10, 另外一个优先级为 30。

通过与链表中的钩子函数的优先级进行对比,发现新的钩子函数应该插入到优先级为 10 的钩子函数后面,所以就 如图3 所示就把新的钩子函数插入到优先级为 10  的钩子函数后面。

五、触发调用钩子函数

钩子函数已经被保存到不同的链上,那么什么时候才会触发调用这些钩子函数来处理数据包呢?

要触发调用某个挂载点上(链)的所有钩子函数,需要使用 NF_HOOK 宏来实现,其定义如下:

// 文件:include/linux/netfilter.h  #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)    \     (list_empty(&nf_hooks[(pf)][(hook)])               \         ? (okfn)(skb)                                  \         : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))

首先介绍一下 NF_HOOK 宏的各个参数的作用:

而 NF_HOOK 宏的实现也比较简单,首先判断一下钩子函数链表是否为空,如果是空的话,就直接调用 okfn 函数来处理数据包,否则就调用  nf_hook_slow 函数来处理数据包。我们来看看 nf_hook_slow 函数的实现:

// 文件:net/core/netfilter.c  int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,                  struct net_device *indev, struct net_device *outdev,                  int (*okfn)(struct sk_buff *)) {     struct list_head *elem;     unsigned int verdict;     int ret = 0;      elem = &nf_hooks[pf][hook]; // 获取要调用的钩子函数链表      // 遍历钩子函数链表,并且调用钩子函数对数据包进行处理     verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev, outdev, &elem, okfn);     ...     // 如果处理结果为 NF_ACCEPT, 表示数据包通过所有钩子函数的处理, 那么就调用 okfn 函数继续处理数据包     // 如果处理结果为 NF_DROP, 表示数据包被拒绝, 应该丢弃此数据包     switch (verdict) {     case NF_ACCEPT:         ret = okfn(skb);         break;     case NF_DROP:         kfree_skb(skb);         ret = -EPERM;         break;     }      return ret; }

nf_hook_slow 函数的实现也比较简单,过程如下:

既然 Netfilter 是通过调用 NF_HOOK 宏来调用钩子函数链表上的钩子函数,那么内核在什么地方调用这个宏呢?

比如数据包进入 IPv4 协议层的处理函数 ip_rcv 函数中就调用了 NF_HOOK 宏来处理数据包,代码如下:

// 文件:net/ipv4/ip_input.c  int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) {     ...     return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); }

如上代码所示,在 ip_rcv 函数中调用了 NF_HOOK 宏来处理输入的数据包,其调用的钩子函数链(挂载点)为 NF_IP_PRE_ROUTING。而  okfn 设置为 ip_rcv_finish,也就是说,当 NF_IP_PRE_ROUTING 链上的所有钩子函数都成功对数据包进行处理后,将会调用  ip_rcv_finish 函数来继续对数据包进行处理。

“如何实现Netfilter”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯