netfilter分析1-钩子函数在内核的初始化

2023-11-23 23:30

本文主要是介绍netfilter分析1-钩子函数在内核的初始化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Linux内核中网络防火墙是通过NF_HOOK宏调用钩子函数进行报文处理,本文基于内核版本4.4对钩子函数的初始化流程进行描述。

以过滤本地报文的钩子函数为例。本地报文过滤钩子函数调用宏:
NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,

       net, NULL, skb, skb->dev, NULL,

       ip_local_deliver_finish);

这个宏在文件/include/linux/netfilter.h中定义:

static inline intNF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb,struct net_device *in, struct net_device *out,int (*okfn)(struct net *, struct sock *, struct sk_buff *)){return NF_HOOK_THRESH(pf, hook, net, sk, skb, in, out, okfn, INT_MIN);}static inline intNF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,struct sk_buff *skb, struct net_device *in,struct net_device *out,int (*okfn)(struct net *, struct sock *, struct sk_buff *),int thresh){int ret = nf_hook_thresh(pf, hook, net, sk, skb, in, out, okfn, thresh);if (ret == 1)ret = okfn(net, sk, skb);return ret;}static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,struct net *net,struct sock *sk,struct sk_buff *skb,struct net_device *indev,struct net_device *outdev,int (*okfn)(struct net *, struct sock *, struct sk_buff *),int thresh){struct list_head *hook_list = &net->nf.hooks[pf][hook];if (nf_hook_list_active(hook_list, pf, hook)) {struct nf_hook_state state;nf_hook_state_init(&state, hook_list, hook, thresh,pf, indev, outdev, sk, net, okfn);return nf_hook_slow(skb, &state);}return 1;}

最终通过链表头struct list_head *hook_list = &net->nf.hooks[pf][hook]这个链表头实际就是所谓的挂载点,后面钩子函数通过链表的形式挂载到这个链表头下

例如过滤本地报文通过遍历执行钩子函数挂载点net->nf.hooks [NFPROTO_IPV4][NF_INET_LOCAL_IN]上的所有钩子函数来实现对报文的过滤处理。

net->nf.hooks [NFPROTO_IPV4][NF_INET_LOCAL_IN]挂载点钩子函数是通过定义在:
net/ipv4/netfilter/iptable_netfilter.c文件中定义的iptable_filter_init进行初始化的

static int __init iptable_filter_init(void){int ret;ret = register_pernet_subsys(&iptable_filter_net_ops);if (ret < 0)return ret;/* Register hooks */filter_ops = xt_hook_link(&packet_filter, iptable_filter_hook);if (IS_ERR(filter_ops)) {ret = PTR_ERR(filter_ops);unregister_pernet_subsys(&iptable_filter_net_ops);}return ret;}

该函数中通过xt_hook_link初始化钩子函数,该函数定义在文件:

net/netfilter/x_tables.c

struct nf_hook_ops *xt_hook_link(const struct xt_table *table, nf_hookfn *fn){unsigned int hook_mask = table->valid_hooks;uint8_t i, num_hooks = hweight32(hook_mask);uint8_t hooknum;struct nf_hook_ops *ops;int ret;ops = kmalloc(sizeof(*ops) * num_hooks, GFP_KERNEL);if (ops == NULL)return ERR_PTR(-ENOMEM);for (i = 0, hooknum = 0; i < num_hooks && hook_mask != 0;hook_mask >>= 1, ++hooknum)                 {if (!(hook_mask & 1))continue;ops[i].hook     = fn;ops[i].pf       = table->af;ops[i].hooknum  = hooknum;ops[i].priority = table->priority;++i;}ret = nf_register_hooks(ops, num_hooks);if (ret < 0) {kfree(ops);return ERR_PTR(ret);}return ops;
}

参数table是对应的filter表,为了不偏离主题,本文只对钩子函数初始化用到的部分进行简单说明,不对表内容展开详细描述,filter表初始化申明如下:

static const struct xt_table packet_filter = {.name = "filter",.valid_hooks = FILTER_VALID_HOOKS,.me = THIS_MODULE,.af = NFPROTO_IPV4,.priority = NF_IP_PRI_FILTER,};

.priority = NF_IP_PRI_FILTER,对钩子函数优先级进行设置。

.valid_hooks = FILTER_VALID_HOOKS,钩子函数会在以下几个点进行挂载:

#define FILTER_VALID_HOOKS ((1 << NF_INET_LOCAL_IN) | \

    (1 << NF_INET_FORWARD) | \

    (1 << NF_INET_LOCAL_OUT))

参数fn是要注册的钩子函数指针,定义如下:

static unsigned intiptable_filter_hook(void *priv, struct sk_buff *skb,const struct nf_hook_state *state){if (state->hook == NF_INET_LOCAL_OUT &&(skb->len < sizeof(struct iphdr) ||ip_hdrlen(skb) < sizeof(struct iphdr)))/* root is playing with raw sockets. */return NF_ACCEPT;return ipt_do_table(skb, state, state->net->ipv4.iptable_filter);}

该函数最终通过ipt_do_table用filter表中的规则对报文进行处理。本文不对表处理进行描述。针对filter表,初始化是只注册了一个相同的钩子函数iptable_filter_hook,该钩子函数注册到了NF_INET_LOCAL_IN、NF_INET_FORWARD、NF_INET_LOCAL_OUT三个挂载点。

钩子函数先存储到struct nf_hook_ops *ops结构中的hook成员,该结构定义如下:

struct nf_hook_ops {struct list_head list;/* User fills in from here down. */nf_hookfn *hook;struct net_device *dev;void *priv;u_int8_t pf;unsigned int hooknum;/* Hooks are ordered in ascending priority. */int priority;};

然后nf_register_hooks函数通过结构中的list将钩子函数分别挂载到上面说的

net->nf.hooks [NFPROTO_IPV4][NF_INET_LOCAL_IN]、

net->nf.hooks [NFPROTO_IPV4][ NF_INET_FORWARD]、

net->nf.hooks [NFPROTO_IPV4][ NF_INET_LOCAL_OUT]、

钩子挂载点。

nf_register_hooks定义在文件/net/netfiler/core.c中

int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n){unsigned int i;int err = 0;for (i = 0; i < n; i++) {err = nf_register_hook(®[i]);if (err)goto err;}return err;err:if (i > 0)nf_unregister_hooks(reg, i);return err;}

进一步通过nf_register_hook对钩子函数进行注册,定义如下:

int nf_register_hook(struct nf_hook_ops *reg){struct net *net, *last;int ret;rtnl_lock();for_each_net(net) {ret = nf_register_net_hook(net, reg);if (ret && ret != -ENOENT)goto rollback;}list_add_tail(®->list, &nf_hook_list);rtnl_unlock();return 0;rollback:last = net;for_each_net(net) {if (net == last)break;nf_unregister_net_hook(net, reg);}rtnl_unlock();return ret;}

其中进一步通过nf_register_net_hook注册到net->nf.hooks[reg->pf][reg->hooknum]挂载点

int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg){struct list_head *hook_list;struct nf_hook_entry *entry;struct nf_hook_ops *elem;entry = kmalloc(sizeof(*entry), GFP_KERNEL);if (!entry)return -ENOMEM;//将nf_hook_ops转存到entty中,这里之所以转存是为了后面方便删除。entry->orig_ops = reg;entry->ops = *reg;hook_list = nf_find_hook_list(net, reg);if (!hook_list) {kfree(entry);return -ENOENT;}mutex_lock(&nf_hook_mutex);list_for_each_entry(elem, hook_list, list) {if (reg->priority < elem->priority)break;}list_add_rcu(&entry->ops.list, elem->list.prev);mutex_unlock(&nf_hook_mutex);#ifdef CONFIG_NETFILTER_INGRESSif (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)net_inc_ingress_queue();#endif#ifdef HAVE_JUMP_LABELstatic_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);#endifreturn 0;}

其中通过nf_find_hook_list找到挂载点,没错就是函数中的&net->nf.hooks[reg->pf][reg->hooknum],终于又遇见你。


static struct list_head *nf_find_hook_list(struct net *net,const struct nf_hook_ops *reg){struct list_head *hook_list = NULL;if (reg->pf != NFPROTO_NETDEV)hook_list = &net->nf.hooks[reg->pf][reg->hooknum];else if (reg->hooknum == NF_NETDEV_INGRESS) {#ifdef CONFIG_NETFILTER_INGRESSif (reg->dev && dev_net(reg->dev) == net)hook_list = ®->dev->nf_hooks_ingress;#endif}return hook_list;}

找到挂载点后就可以对钩子函数进行挂载了,挂载过程需要枷锁,然后挂载到对应优先级所在的位置,遍历执行钩子函数时是按优先级执行的,钩子函数中priority越小优先级越高,因此钩子函数也是按照优先级从低到高进行挂载。挂载过程很简单,如下所示:

mutex_lock(&nf_hook_mutex);list_for_each_entry(elem, hook_list, list) {if (reg->priority < elem->priority)break;}list_add_rcu(&entry->ops.list, elem->list.prev);mutex_unlock(&nf_hook_mutex);

到此钩子函数初始化就结束了,通过上面的分析可以得出钩子函数、filter表关系如下图:

该图表示针对NF_INET_LOCAL_IN、NF_INET_FORWARD、NF_INET_LOCAL_OUT挂载点注册了优先级为NF_IP_PRI_FILTER 的钩子函数:iptable_filter_hook,该钩子函数通过对将报文根据filter表中的规则进行处理。

这篇关于netfilter分析1-钩子函数在内核的初始化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/420852

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数