Linux协议栈-netfilter(2)-conntrack

连接跟踪(conntrack)用来跟踪和记录一个连接的状态,它为经过协议栈的数据包记录状态,这为防火墙检测连接状态提供了参考,同时在数据包需要做NAT时也为转换工作提供便利。

本文基于Linux内核2.6.31实现的conntrack进行源码分析。

1. conntrack模块初始化

1.1 conntrack模块入口

conntrack模块的初始化主要就是为必要的全局数据结构进行初始化,代码流程如下:


上面这些函数只是简单的调用关系,下面来看具体实现。

nf_conntrack_init_init_net()函数:

1. 根据内存的大小给全局变量nf_conntrack_htable_size和nf_conntrack_max赋值,他们分别指定了存放连接跟踪条目的hash表的大小以及系统可以创建的连接跟踪条目的数量。

2. 为nf_conn结构申请slab cache:

nf_conntrack_cachep= kmem_cache_create("nf_conntrack",

                             sizeof(struct nf_conn), 0, SLAB_DESTROY_BY_RCU, NULL);

这个缓存用于新建struct nf_conn结构,每个连接的状态都由这样一个结构体实例来描述,每个连接都分为original和reply两个方向,每个方向都用一个元组(tuple)表示,tuple中包含了这个方向上数据包的信息,如源IP、目的IP、源port、目的port等。

3. 给全局的nf_ct_l3protos[]赋上默认值:

nf_ct_l3protos[]数组中的每个元素都赋值为nf_conntrack_l3proto_generic,即不区分L3协议的处理函数,后续的初始化会为不同的L3协议赋上相应的值(例如1.2节初始化特定于IPv4协议的conntrack的时候)。由下面的定义可知这是一组操作函数。

该全局数据的定义为:

struct nf_conntrack_l3proto * nf_ct_l3protos[AF_MAX]__read_mostly;

struct nf_conntrack_l3proto nf_conntrack_l3proto_generic __read_mostly = {
	.l3proto	 = PF_UNSPEC,
	.name		 = "unknown",
	.pkt_to_tuple	 = generic_pkt_to_tuple,
	.invert_tuple	 = generic_invert_tuple,
	.print_tuple	 = generic_print_tuple,
	.get_l4proto	 = generic_get_l4proto,
};

4. 给全局的hash表nf_ct_helper_hash分配一个页大小的空间,通过nf_ct_alloc_hashtable()分配。

static struct hlist_head *nf_ct_helper_hash__read_mostly;

这个hash表用于存放helper类型的conntrack extension(见2.3节)。

这里要注意两个地方:

1) sizeof(struct hlist_nulls_head)和 sizeof(struct hlist_head)大小必须相等。

2) roundup(x, y)宏定义,这个宏保证了分配整个页的空间。

#defineroundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))

意思是要把x调整为y的整数倍的值,如x=46,y=32,则返回64。

5. 把helper_extend注册进全局数组nf_ct_ext_types[]中,并调整helper_extend.alloc_size。

struct nf_ct_ext_type *nf_ct_ext_types[NF_CT_EXT_NUM];
static struct nf_ct_ext_type helper_extend __read_mostly = {
	.len	= sizeof(struct nf_conn_help),
	.align	= __alignof__(struct nf_conn_help),
	.id	= NF_CT_EXT_HELPER,
};

conntrack extentions共有4种类型,即全局数组nf_ct_ext_types[]的元素个数,每个元素的含义如下定义:

enum nf_ct_ext_id
{
	NF_CT_EXT_HELPER,
	NF_CT_EXT_NAT,
	NF_CT_EXT_ACCT,
	NF_CT_EXT_ECACHE,
	NF_CT_EXT_NUM,
};

struct nf_ct_ext_type结构定义如下:

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;

	unsigned int flags;

	/* Length and min alignment. */
	u8 len;
	u8 align;
	/* initial size of nf_ct_ext. */
	u8 alloc_size;
};

nf_conntrack_init_net()函数:

该函数用来初始化net->ct成员。net是本地CPU的网络命名空间,在单CPU系统中就是全局变量init_net。它的ct成员的定义如下:

struct netns_ct {
	atomic_t		count;
	unsigned int		expect_count;
	struct hlist_nulls_head	*hash;
	struct hlist_head	*expect_hash;
	struct hlist_nulls_head	unconfirmed;
	struct hlist_nulls_head	dying;
	struct ip_conntrack_stat *stat;
	int			sysctl_events;
	unsigned int		sysctl_events_retry_timeout;
	int			sysctl_acct;
	int			sysctl_checksum;
	unsigned int		sysctl_log_invalid; /* Log invalid packets */
#ifdef CONFIG_SYSCTL
	struct ctl_table_header	*sysctl_header;
	struct ctl_table_header	*acct_sysctl_header;
	struct ctl_table_header	*event_sysctl_header;
#endif
	int			hash_vmalloc;
	int			expect_vmalloc;
};

函数分析:

static int nf_conntrack_init_net(struct net *net)
{
	int ret;

	atomic_set(&net->ct.count, 0); /* 设置计数器为0 */

	/* 初始化unconfirmed链表 */
	INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, UNCONFIRMED_NULLS_VAL);

	/* 初始化dying链表 */
	INIT_HLIST_NULLS_HEAD(&net->ct.dying, DYING_NULLS_VAL); 

	/* 给net->ct.stat分配空间并清0。统计数据 */
	net->ct.stat = alloc_percpu(struct ip_conntrack_stat); 
	if (!net->ct.stat) {
		ret = -ENOMEM;
		goto err_stat;
	}
	/* 初始化conntrack hash table,表的大小上面已初始化过了。同样的,这里会调整nf_conntrack_htable_size为页大小的整数倍。 */
	net->ct.hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size, 
					     &net->ct.hash_vmalloc, 1);
	if (!net->ct.hash) {
		ret = -ENOMEM;
		printk(KERN_ERR "Unable to create nf_conntrack_hash\n");
		goto err_hash;
	}

	/* 初始化net->ct.expect_hash及缓存,并在/proc中创建相应文件,下面会单独说明该函数。 */
	ret = nf_conntrack_expect_init(net);
	if (ret < 0)
		goto err_expect;

	/* 将acct_extend注册进全局数组nf_ct_ext_types[NF_CT_EXT_ACCT]中。 */
	ret = nf_conntrack_acct_init(net);
	if (ret < 0)
		goto err_acct;
	ret = nf_conntrack_ecache_init(net); /* event cache of netfilter */
	if (ret < 0)
		goto err_ecache;

	/* 单独的fake conntrack */
	/* Set up fake conntrack:
	    - to never be deleted, not in any hashes */
	atomic_set(&nf_conntrack_untracked.ct_general.use, 1);
	/*  - and look it like as a confirmed connection */
	set_bit(IPS_CONFIRMED_BIT, &nf_conntrack_untracked.status);

	return 0;
}

nf_conntrack_expect_init()函数,

int nf_conntrack_expect_init(struct net *net)
{
	int err = -ENOMEM;

	if (net_eq(net, &init_net)) {
		if (!nf_ct_expect_hsize) {
			/* 期望连接hash table的size */
			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; 
	}

	/* 分配期望连接hash table。 */
	net->ct.expect_count = 0;
	net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize,
						  &net->ct.expect_vmalloc, 0);
	if (net->ct.expect_hash == NULL)
		goto err1;

	if (net_eq(net, &init_net)) {
		/* 期望连接的slab cache。 */
		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(net);
	if (err < 0)
		goto err3;

	return 0;
}

需要先说一下struct hlist_nulls_head结构以及INIT_HLIST_NULLS_HEAD宏:

/*
 * Special version of lists, where end of list is not a NULL pointer,
 * but a 'nulls' marker, which can have many different values.
 * (up to 2^31 different values guaranteed on all platforms)
 *
 * In the standard hlist, termination of a list is the NULL pointer.
 * In this special 'nulls' variant, we use the fact that objects stored in
 * a list are aligned on a word (4 or 8 bytes alignment).
 * We therefore use the last significant bit of 'ptr' :
 * Set to 1 : This is a 'nulls' end-of-list marker (ptr >> 1)
 * Set to 0 : This is a pointer to some object (ptr)
 */

struct hlist_nulls_head {
	struct hlist_nulls_node *first;
};

struct hlist_nulls_node {
	struct hlist_nulls_node *next, **pprev;
};

/* 注意右值是first里面的值而不是地址。这个值都是奇数,即二进制的最后一位是1;
在普通链表中一般用NULL来判断链表结束,而linux内核中由于地址都是4或8字节对齐的,所以指针的末两位用不上,因此可以复用一下,最后一位是1表示链表结束,是0则未结束。
这个宏保证了最末位是1,而其他位可以是随意值,所以可以有2^31种可能值。

下面是对HLIST_NULLS_HEAD链表的初始化,所以给first赋值。
*
  • 4
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
2023年的"Ja-Netfilter"是一个基于网络过滤技术的创新想法。它旨在提供更高效、更智能的网络过滤解决方案,帮助用户更好地管理和保护他们的网络安全。 "Ja-Netfilter"将利用最新的人工智能和机器学习技术,通过深度学习算法来分析和识别网络流量中的恶意行为和威胁。它将建立起一种自适应的过滤系统,能够根据实时数据和情境自动地调整过滤规则,以应对不断变化的网络攻击和威胁。 与传统的网络过滤技术相比,"Ja-Netfilter"具有明显的优势。首先,它能够从大量的网络数据中快速准确地分析出潜在的威胁,大大提高了网络防御的效率。其次,通过使用机器学习算法,"Ja-Netfilter"能够主动学习和适应新的网络攻击手段,及时更新过滤规则,从而提供更好的保护。此外,"Ja-Netfilter"还支持用户自定义的过滤规则,使用户能够根据自己的需求对网络流量进行精细化的控制。 "Ja-Netfilter"除了为个人用户提供网络安全保护外,也可以应用于企业和组织的网络安全管理中。它可以帮助企业对内部和外部网络流量进行监控和过滤,实时检测和阻止潜在的威胁,从而保护企业机密和客户数据的安全。 总而言之,"Ja-Netfilter"是一项创新的网络过滤技术,将利用人工智能和机器学习技术提供更高效、更智能的网络安全保护。它的出现将有助于提升网络安全防御能力,为用户提供更安全、更稳定的网络环境。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值