我是靠谱客的博主 幸福哈密瓜,最近开发中收集的这篇文章主要介绍连接跟踪子系统之期望连接1. 期望连接跟踪信息块2. 期望连接跟踪信息块的插入3. 期望连接子模块初始化4. 期望连接跟踪信息块的匹配,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
这篇笔记记录了连接跟踪子系统中期望连接相关的内容,这部分内容比较抽象,自己目前也只是基于ftp协议的使用来理解的,对于其它应用场景尚未接触过,尚不理解。期望连接相关代码文件有:
代码路径 | 说明 |
---|---|
/net/netfilter/nf_conntrack_expect.c | 期望连接子模块实现文件 |
1. 期望连接跟踪信息块
期望连接跟踪信息块用struct nf_conntrack_expect对象表示,该结构定义如下:
struct nf_conntrack_expect
{
//同属一个master连接的期望连接被组织到一个链表中,见struct nf_conn_help定义
struct hlist_node lnode;
//用于将期望连接跟踪信息块加入到全局的哈希表中
struct hlist_node hnode;
//该期望连接跟踪信息块能够匹配的skb
struct nf_conntrack_tuple tuple;
struct nf_conntrack_tuple_mask mask;
//当期望连接到来时,会调用该函数,该回调函数是可选的
void (*expectfn)(struct nf_conn *new, struct nf_conntrack_expect *this);
//指向识别出该期望连接的helper
struct nf_conntrack_helper *helper;
//指向master连接跟踪信息块
struct nf_conn *master;
//和普通的连接类似,每个期望连接也有有效定时器,定时器超时则会从系统中删除该期望连接
struct timer_list timeout;
//引用计数
atomic_t use;
/* Flags */
unsigned int flags;
#ifdef CONFIG_NF_NAT_NEEDED
__be32 saved_ip;
/* This is the original per-proto part, used to map the
* expected connection the way the recipient expects. */
union nf_conntrack_man_proto saved_proto;
/* Direction relative to the master connection. */
enum ip_conntrack_dir dir;
#endif
struct rcu_head rcu;
};
2. 期望连接跟踪信息块的插入
当helper识别到即将要有一个期望连接到达时,就会创建一个期望连接跟踪信息块,然后将其加入到全局哈希表中,这样等真正的期望连接的skb到达时,就可以匹配该期望连接跟踪信息块,表示期望连接建立成功。
int nf_ct_expect_related(struct nf_conntrack_expect *expect)
{
struct nf_conntrack_expect *i;
struct nf_conn *master = expect->master;
//找到master连接的help扩展
struct nf_conn_help *master_help = nfct_help(master);
struct hlist_node *n;
unsigned int h;
int ret;
NF_CT_ASSERT(master_help);
spin_lock_bh(&nf_conntrack_lock);
//个人理解,这里只是一种检查,确保对应的helper存在
if (!master_help->helper) {
ret = -ESHUTDOWN;
goto out;
}
//计算该期望连接的哈希值
h = nf_ct_expect_dst_hash(&expect->tuple);
//查找对应的冲突链,检查该期望连接是否已经存在,如果已经存在(如重传包导致的重复识别),
//更新保活定时器,不存在则做个clash检查(不知道是干什么的)
hlist_for_each_entry(i, n, &nf_ct_expect_hash[h], hnode) {
if (expect_matches(i, expect)) {
/* Refresh timer: if it's dying, ignore.. */
if (refresh_timer(i)) {
ret = 0;
goto out;
}
} else if (expect_clash(i, expect)) {
ret = -EBUSY;
goto out;
}
}
//检查该helper对象的最大允许期望连接数目是否还允许新增期望连接
if (master_help->helper->max_expected && master_help->expecting >= master_help->helper->max_expected)
evict_oldest_expect(master);
//检查系统的最大允许期望丽娜姐数目是否允许新增期望连接
if (nf_ct_expect_count >= nf_ct_expect_max) {
if (net_ratelimit())
printk(KERN_WARNING "nf_conntrack: expectation table fulln");
ret = -EMFILE;
goto out;
}
//将该期望连接信息块插入全局哈希表中进行跟踪,这样当属于该期望连接的skb来到后,就能正确匹配到
nf_ct_expect_insert(expect);
nf_ct_expect_event(IPEXP_NEW, expect);
ret = 0;
out:
spin_unlock_bh(&nf_conntrack_lock);
return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_expect_related);
3. 期望连接子模块初始化
//期望连接哈希表的哈希桶大小可以是模块参数
module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0600);
int __init nf_conntrack_expect_init(void)
{
int err = -ENOMEM;
//如果没有指定模块参数,那么按照连接跟踪信息块哈希表的大小设置,
//最小值为1,此时哈希表退化为双列表
if (!nf_ct_expect_hsize) {
nf_ct_expect_hsize = nf_conntrack_htable_size / 256;
if (!nf_ct_expect_hsize)
nf_ct_expect_hsize = 1;
}
//连接跟踪的最大次数
nf_ct_expect_max = nf_ct_expect_hsize * 4;
//为期望连接跟踪信息块哈希表分配内存
nf_ct_expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, &nf_ct_expect_vmalloc);
if (nf_ct_expect_hash == NULL)
goto err1;
//创建高速缓存
nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
sizeof(struct nf_conntrack_expect), 0, 0, NULL);
if (!nf_ct_expect_cachep)
goto err2;
//创建/proc/net/nf_conntrack_expect结点,通过该结点用户空间可以
//获取当前系统中所有的期望连接。
err = exp_proc_init();
if (err < 0)
goto err3;
return 0;
...
}
期望连接子模块的初始化就干了三件事:
- 分配了一个哈希表用于保存期望连接:即struct nf_conntrack_expect对象;
- 创建一个高速缓存用于分配struct nf_conntrack_expect对象;
- 在/proc文件系统中创建期望连接相关的结点。
4. 期望连接跟踪信息块的匹配
在init_conntrack()中,根据skb创建了新的连接跟踪信息块后,会检查该新的连接是否是某个连接的期望连接,相关代码如下:
static struct nf_conntrack_tuple_hash *
init_conntrack(const struct nf_conntrack_tuple *tuple, struct nf_conntrack_l3proto *l3proto,
struct nf_conntrack_l4proto *l4proto, struct sk_buff *skb, unsigned int dataoff)
{
struct nf_conn *ct;
struct nf_conn_help *help;
struct nf_conntrack_tuple repl_tuple;
struct nf_conntrack_expect *exp;
...
spin_lock_bh(&nf_conntrack_lock);
exp = nf_ct_find_expectation(tuple);
if (exp) {
pr_debug("conntrack: expectation arrives ct=%p exp=%pn", ct, exp);
//设置IPS_EXPECTED_BIT表示该连接是一个期望连接
__set_bit(IPS_EXPECTED_BIT, &ct->status);
//连接跟踪信息块的master指针指向其master连接
ct->master = exp->master;
//类似于非期望连接的处理,见"连接跟踪子系统之helper"
if (exp->helper) {
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (help)
rcu_assign_pointer(help->helper, exp->helper);
}
#ifdef CONFIG_NF_CONNTRACK_MARK
ct->mark = exp->master->mark;
#endif
#ifdef CONFIG_NF_CONNTRACK_SECMARK
ct->secmark = exp->master->secmark;
#endif
//增加master连接的引用计数
nf_conntrack_get(&ct->master->ct_general);
NF_CT_STAT_INC(expect_new);
} else {
...
}
...
//调用期望连接跟踪信息块的expecfn()回调,该回调函数是可选的
if (exp) {
if (exp->expectfn)
exp->expectfn(ct, exp);
nf_ct_expect_put(exp);
}
return &ct->tuplehash[IP_CT_DIR_ORIGINAL];
}
4.1 期望连接跟踪信息块的查找
/* If an expectation for this connection is found, it gets delete from
* global list then returned. */
struct nf_conntrack_expect* nf_ct_find_expectation(const struct nf_conntrack_tuple *tuple)
{
struct nf_conntrack_expect *exp;
//在全局的期望连接跟踪信息块哈希表中查找匹配的tuple
exp = __nf_ct_expect_find(tuple);
if (!exp)
return NULL;
//如果master连接还没有被确认,那么不处理这种期望连接
if (!nf_ct_is_confirmed(exp->master))
return NULL;
//一般来说,期望连接跟踪信息块对应的期望连接到达后,就会将其从全局哈希表中删除,但是对于设置了
//NF_CT_EXPECT_PERMANENT的特殊处理,不会删除
if (exp->flags & NF_CT_EXPECT_PERMANENT) {
atomic_inc(&exp->use);
return exp;
} else if (del_timer(&exp->timeout)) {
nf_ct_unlink_expect(exp);
return exp;
}
return NULL;
}
最后
以上就是幸福哈密瓜为你收集整理的连接跟踪子系统之期望连接1. 期望连接跟踪信息块2. 期望连接跟踪信息块的插入3. 期望连接子模块初始化4. 期望连接跟踪信息块的匹配的全部内容,希望文章能够帮你解决连接跟踪子系统之期望连接1. 期望连接跟踪信息块2. 期望连接跟踪信息块的插入3. 期望连接子模块初始化4. 期望连接跟踪信息块的匹配所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复