在上一篇笔记Netfilter之table、rule、match、target数据结构中,看到了内核对这些对象的定义,这篇笔记记录了内核对这些数据结构的组织,以及一些相关的核心函数分析。设计的代码文件主要有:
代码路径说明/net/netfilter/x_tables.cnetfilter框架对table、match、target的核心实现如下,struct net中的xt成员为每个协议族维护了一个链表,用来组织该协议族注册的table,具体的协议族都是在协议族初始化过程中向框架注册自己的table。
struct net { ... #ifdef CONFIG_NETFILTER struct netns_xt xt; #endif }; struct netns_xt { struct list_head tables[NPROTO]; };注册之前,需要先看看struct xt_table_info对象的分配,因为该对象才是真正保存表内容的结构。
上面很重要的一点就是表中的规则对于每个CPU都有一份相同的拷贝,这样可以避免多个CPU在访问规则的时候进行额外的锁操作。
各协议族调用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);该函数用@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(),就是注册的反操作,不再赘述。
如下,全局的struct xt_af变量维护了两个链表,分别来保存系统中已注册match和target,链表中的元素就是struct xt_match和struct xt_target。
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()。
和match类似,target也有xt_register_targets()、xt_unregister_target()和xt_unregister_targets()接口。