Netfilter之table、rule、match、target的组织管理

本文详细介绍了Netfilter中table、rule、match、target的组织管理,包括table的注册(xt_alloc_table_info()、xt_register_table()、xt_replace_table())、match和target的管理(xt_af链表、xt_register_matches()、xt_register_targets()),以及模块初始化的相关内容。通过这些核心函数,理解内核如何维护Netfilter的各种组件。
摘要由CSDN通过智能技术生成

在上一篇笔记Netfilter之table、rule、match、target数据结构中,看到了内核对这些对象的定义,这篇笔记记录了内核对这些数据结构的组织,以及一些相关的核心函数分析。设计的代码文件主要有:

代码路径说明
/net/netfilter/x_tables.cnetfilter框架对table、match、target的核心实现

1. table的管理

如下,struct net中的xt成员为每个协议族维护了一个链表,用来组织该协议族注册的table,具体的协议族都是在协议族初始化过程中向框架注册自己的table。

struct net {
...
#ifdef CONFIG_NETFILTER
	struct netns_xt		xt;
#endif
};
struct netns_xt {
	struct list_head tables[NPROTO];
};

1.1 table的注册

注册之前,需要先看看struct xt_table_info对象的分配,因为该对象才是真正保存表内容的结构。

1.1.1 xt_alloc_table_info()

//size表示table中规则需要占用的内存大小
struct xt_table_info *xt_alloc_table_info(unsigned int size)
{
	struct xt_table_info *newinfo;
	int cpu;
	//实际分配的内存需要按照页边界对齐
	if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > num_physpages)
		return NULL;
	//为struct xt_table_info对象本身分配内存,不包括规则占用部分
	newinfo = kzalloc(XT_TABLE_INFO_SZ, GFP_KERNEL);
	if (!newinfo)
		return NULL;
	newinfo->size = size;
    //为每个CPU都分配一块内存保存规则,每块内存用entries[i]指针索引
	for_each_possible_cpu(cpu) {
		if (size <= PAGE_SIZE)
			newinfo->entries[cpu] = kmalloc_node(size, GFP_KERNEL, cpu_to_node(cpu));
		else
			newinfo->entries[cpu] = vmalloc_node(size, cpu_to_node(cpu));
		if (newinfo->entries[cpu] == NULL) {
			xt_free_table_info(newinfo);
			return NULL;
		}
	}
	return newinfo;
}
EXPORT_SYMBOL(xt_alloc_table_info);

上面很重要的一点就是表中的规则对于每个CPU都有一份相同的拷贝,这样可以避免多个CPU在访问规则的时候进行额外的锁操作。

1.1.2 xt_register_table()

各协议族调用xt_register_table()向框架注册自己的table。bootstrap参数的存在仅仅是为了复用xt_replace_table()的逻辑而已,因为xt_replace_table()函数本身是用来做table替换的。

struct xt_table *xt_register_table(struct net *net, struct xt_table *table,
	struct xt_table_info *bootstrap, struct xt_table_info *newinfo)
{
	int ret;
	struct xt_table_info *private;
	struct xt_table *t;

	//将参数指定的table拷贝一份
	table = kmemdup(table, sizeof(struct xt_table), GFP_KERNEL);
	if (!table) {
		ret = -ENOMEM;
		goto out;
	}
	//如上所述,全局数组xt[table->af]保存的该协议族的所有match和target,
	//但是这里持锁后访问了net->xt.tables[table->af]变量,该变量保存的是
	//该协议族所有的table。这把锁的定义位置并不合适,其本意是要保护table、
	//match、target,但是table的定义却在net中(更高版本已将match和target
	//也移到了net中)
	ret = mutex_lock_interruptible(&xt[table->af].mutex);
	if (ret != 0)
		goto out_free;
	//检查该协议族的已定义table中是否已有指定名字的table,可见表名字需要协议族内部唯一
	list_for_each_entry(t, &net->xt.tables[table->af], list) {
		if (strcmp(t->name, table->name) == 0) {
			ret = -EEXIST;
			goto unlock;
		}
	}
	/* Simplifies replace_table code. */
	table->private = bootstrap;
	rwlock_init(&table->lock);
	//用newinfo替换table中原来的private指针
	if (!xt_replace_table(table, 0, newinfo, &ret))
		goto unlock;
	private = table->private;
	duprintf("table->private->number = %u\n", private->number);
	/* save number of initial entries */
	private->initial_entries = private->number;
	//将table加入到该协议族的table集合中,注册完毕
	list_add(&table->list, &net->xt.tables[table->af]);
	mutex_unlock(&xt[table->af].mutex);
	return table;
unlock:
	mutex_unlock(&xt[table->af].mutex);
out_free:
	kfree(table);
out:
	return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(xt_register_table);

1.1.3 xt_replace_table()

该函数用@newinfo替换@table中原来的private指向的内容,参数num_counters必须是原来table中规则的数目,否则替换将失败。

struct xt_table_info* xt_replace_table(struct xt_table *table, 
	unsigned int num_counters, struct xt_table_info *newinfo, int *error)
{
	struct xt_table_info *oldinfo, *private;
	/* Do the substitution. */
	write_lock_bh(&table->lock);
	private = table->private;
	//个人理解:这只是一种简单的安全检查
	if (num_counters != private->number) {
		write_unlock_bh(&table->lock);
		*error = -EAGAIN;
		return NULL;
	}
	//替换privaet指针
	oldinfo = private;
	table->private = newinfo;
	//初始规则数目保持和旧的相同
	newinfo->initial_entries = oldinfo->initial_entries;
	write_unlock_bh(&table->lock);
	//返回旧的private指针
	return oldinfo;
}
EXPORT_SYMBOL_GPL(xt_replace_table);

关于table的注册需要多啰嗦几句,个人理解:xt_regitster_table()之所以需要两个struct xt_table_info参数,是因为table的注册过程要复用函数xt_replace_table(),该函数本身是用来做table替换的。

表的去注册就是xt_unregister_table(),就是注册的反操作,不再赘述。

2. match和target的管理

如下,全局的struct xt_af变量维护了两个链表,分别来保存系统中已注册match和target,链表中的元素就是struct xt_match和struct xt_target。

2.1 struct xt_af

//全局变量xt会保存系统中所有协议族的match和target
struct xt_af {
	struct mutex mutex;
	struct list_head match;
	struct list_head target;
};
static struct xt_af *xt;

2.2.match的注册

match的注册就相当简单了,直接将struct xt_match加入到对应协议族的match链表尾部。

int xt_register_match(struct xt_match *match)
{
	int ret, af = match->family;
	//和table一样,match用xt[af].mutext保护
	ret = mutex_lock_interruptible(&xt[af].mutex);
	if (ret != 0)
		return ret;
	//match链接到链表尾部
	list_add(&match->list, &xt[af].match);
	mutex_unlock(&xt[af].mutex);
	return ret;
}
EXPORT_SYMBOL(xt_register_match);

也可以通过xt_register_matches()一次注册多个match。相应的,去注册函数有xt_unregister_match()和xt_unregister_matches()。

2.3 target的注册

int xt_register_target(struct xt_target *target)
{
	int ret, af = target->family;
	ret = mutex_lock_interruptible(&xt[af].mutex);
	if (ret != 0)
		return ret;
	list_add(&target->list, &xt[af].target);
	mutex_unlock(&xt[af].mutex);
	return ret;
}
EXPORT_SYMBOL(xt_register_target);

和match类似,target也有xt_register_targets()、xt_unregister_target()和xt_unregister_targets()接口。

3 模块初始化

static int __net_init xt_net_init(struct net *net)
{
	int i;
	//初始化所有协议族保存table的链表
	for (i = 0; i < NPROTO; i++)
		INIT_LIST_HEAD(&net->xt.tables[i]);
	return 0;
}
static struct pernet_operations xt_net_ops = {
	.init = xt_net_init,
};

static int __init xt_init(void)
{
	int i, rv;
	//各个协议族分别和xt[pf]对应
	xt = kmalloc(sizeof(struct xt_af) * NPROTO, GFP_KERNEL);
	if (!xt)
		return -ENOMEM;
	//初始化锁、target链表、match链表
	for (i = 0; i < NPROTO; i++) {
		mutex_init(&xt[i].mutex);
		INIT_LIST_HEAD(&xt[i].target);
		INIT_LIST_HEAD(&xt[i].match);
	}
	rv = register_pernet_subsys(&xt_net_ops);
	if (rv < 0)
		kfree(xt);
	return rv;
}
module_init(xt_init);
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值