2018年(21)
分类: LINUX
2018-09-19 15:48:57
上篇我们举例看了一下大致一个conntrack的建立过程。在我们开始分析实现上述过程的源码前,先来看一看conntrack提供的功能扩展机制,因为类似tftp helper功能是基于这个扩展机制来实现的。
该功能的代码源文件在如下路径:
net/netfilter/nf_conntrack_extend.c
include/net/netfilter/nf_conntrack_extend.h
数据结构图
如图,每个连接conntrack结构体有一个指针ext,指向一个扩展功能结构体。该结构体下带着一些扩展功能的私有数据。根据这些功能的私有数据来进行扩展功能的处理。
定义一个扩展:
struct nf_ct_ext_type
{
/* Destroys relationships (can be NULL). */
void (*destroy)(struct nf_conn *ct);
/* Called when realloacted (can be NULL).
Contents has already been moved. */
void (*move)(void *new, void *old);
enum nf_ct_ext_id id; //标识扩展功能的id
unsigned int flags;
/* Length and min alignment. */
u8 len; //扩展功能私有数据的长度
u8 align; //扩展功能私有数据的对齐字节
/* initial size of nf_ct_ext. */
//给连接添加第一个扩展功能时,这时struct nf_ct_ext还没创建,
需要申请struct nf_ct_ext 和扩展功能私有数据一起的内存大小。
u8 alloc_size;
};
每种类型的扩展需要实例化一个struct nf_ct_ext_type结构体,并注册到Netfilter管理的数组中。这样每次给连接添加扩展功能时,根据要添加的扩展功能id,找到对应的struct nf_ct_ext_type实例,根据实例中的数值来申请内存并初始化私有数据。
Netfilter中已定义的扩展功能
enum nf_ct_ext_id
{
NF_CT_EXT_HELPER, //helper机制
NF_CT_EXT_NAT,//网络地址转换
NF_CT_EXT_ACCT,//连接统计计数
NF_CT_EXT_ECACHE,//Netfilter事件通知机制
NF_CT_EXT_NUM,
};
注册扩展功能
int nf_ct_extend_register(struct nf_ct_ext_type *type)
{
int ret = 0;
mutex_lock(&nf_ct_ext_type_mutex);
if (nf_ct_ext_types[type->id]) {
ret = -EBUSY;
goto out;
}
/* This ensures that nf_ct_ext_create() can allocate enough area
before updating alloc_size */
/*根据扩展功能的私有数据长度初始化alloc_size*/
type->alloc_size = ALIGN(sizeof(struct nf_ct_ext), type->align) + type->len;
/*根据扩展功能的id号,加入Netfilter管理的全局数组中*/
rcu_assign_pointer(nf_ct_ext_types[type->id], type);
/*根据Netfilter中已经注册了的扩展功能类型,重新设置所有已注册的扩展功能类型的
alloc_size字段。这样每次第一次给连接申请扩展功能的内存时,就可以把Netfilter中
已注册的所有扩展功能的内存申请好了,防止每次给连接添加扩展功能时重复申请内
存,提高处理速度*/
update_alloc_size(type);
out:
mutex_unlock(&nf_ct_ext_type_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_extend_register);
static void update_alloc_size(struct nf_ct_ext_type *type)
{
int i, j;
struct nf_ct_ext_type *t1, *t2;
enum nf_ct_ext_id min = 0, max = NF_CT_EXT_NUM - 1;
/* unnecessary to update all types */
/*如果该扩展功能需要每次添加时要重新申请内存,
就不用更新其他扩展功能类型的alloc_size,只需更新自己的即可*/
if ((type->flags & NF_CT_EXT_F_PREALLOC) == 0) {
min = type->id;
max = type->id;
}
/* This assumes that extended areas in conntrack for the types
whose NF_CT_EXT_F_PREALLOC bit set are allocated in order */
/*循环操作,更新所有的已注册的扩展功能类型的alloc_size*/
for (i = min; i <= max; i++) {
t1 = nf_ct_ext_types[i];
if (!t1)
continue;
t1->alloc_size = sizeof(struct nf_ct_ext) + ALIGN(sizeof(struct nf_ct_ext), t1->align) + t1->len;
for (j = 0; j < NF_CT_EXT_NUM; j++) {
t2 = nf_ct_ext_types[j];
if (t2 == NULL || t2 == t1 || (t2->flags & NF_CT_EXT_F_PREALLOC) == 0)
continue;
t1->alloc_size = ALIGN(t1->alloc_size, t2->align) + t2->len;
}
}
}
给连接添加扩展功能
#define NF_CT_EXT_HELPER_TYPE struct nf_conn_help
#define NF_CT_EXT_NAT_TYPE struct nf_conn_nat
#define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter
#define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache
#define nf_ct_ext_add(ct, id, gfp) \
((id##_TYPE *)__nf_ct_ext_add((ct), (id), (gfp)))
void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
{
struct nf_ct_ext *new;
int i, newlen, newoff;
struct nf_ct_ext_type *t;
/* Conntrack must not be confirmed to avoid races on reallocation. */
/*给conntrack添加扩展功能时,该conntrak必须是没被确认过得状态*/
NF_CT_ASSERT(!nf_ct_is_confirmed(ct));
/*如果该conntrack的扩展功能的内存还没用申请,就申请内存并添加该扩展功能*/
if (!ct->ext)
return nf_ct_ext_create(&ct->ext, id, gfp);
/*如果该contrack的扩展功能内存已经申请好了,就不需要再处理了*/
if (nf_ct_ext_exist(ct, id))
return NULL;
/*扩展功能内存还没申请好,需要重新申请新的内存,
把已经初始化好的其他扩展功能数据重新添加到新的内存中*/
rcu_read_lock();
t = rcu_dereference(nf_ct_ext_types[id]);
BUG_ON(t == NULL);
newoff = ALIGN(ct->ext->len, t->align);
newlen = newoff + t->len;
rcu_read_unlock();
new = __krealloc(ct->ext, newlen, gfp);
if (!new)
return NULL;
if (new != ct->ext)
{
for (i = 0; i < NF_CT_EXT_NUM; i++)
{
if (!nf_ct_ext_exist(ct, i))
continue;
rcu_read_lock();
t = rcu_dereference(nf_ct_ext_types[i]);
if (t && t->move)
t->move((void *)new + new->offset[i],
(void *)ct->ext + ct->ext->offset[i]);
rcu_read_unlock();
}
/*释放旧的扩展功能内存*/
call_rcu(&ct->ext->rcu, __nf_ct_ext_free_rcu);
/*替换旧的扩展功能内存*/
ct->ext = new;
}
new->offset[id] = newoff;
new->len = newlen;
memset((void *)new + newoff, 0, newlen - newoff);
return (void *)new + newoff;
}
EXPORT_SYMBOL(__nf_ct_ext_add);
具体的添加操作在conntrack的建立和处理过程中添加,比如添加相应的helper扩展功能时,调用如下
nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp);
(未完待续)