连接跟踪子系统之期望连接

这篇博客深入探讨了连接跟踪子系统中的期望连接,包括期望连接跟踪信息块的结构、插入方法、子模块初始化过程以及信息块的匹配策略。通过分析ftp协议的应用,解释了期望连接在连接跟踪中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇笔记记录了连接跟踪子系统中期望连接相关的内容,这部分内容比较抽象,自己目前也只是基于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 full\n");
		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;
...
}

期望连接子模块的初始化就干了三件事:

  1. 分配了一个哈希表用于保存期望连接:即struct nf_conntrack_expect对象;
  2. 创建一个高速缓存用于分配struct nf_conntrack_expect对象;
  3. 在/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=%p\n", 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值