netfilter之nat初始化

nat功能是我们使用非常广泛,包括SNAT、DNAT,很多功能是基于NAT实现。

(1)SNAT

SNAT简称源地址转换,比如一个公司只有一个共有IP,公司下面有许多私有设备,私有设备都分配了私有地址,私有地址不能在互联网中传递,那么就要做源地址转换,私有设备要访问互联网就会把源地址转换为公司的共有IP,通过这个共有IP访问互联网。

(2)DNAT

DNAT简称目的地址转换,也就是把目的地址转换为其他地址,这个是DMZ、端口映射的原理。比如一个公司的一些重要数据要给外网访问, 为了安全不直接部署在共有IP地址上,而是部署在公司内网的一个私有IP,外网访问公司的这个共有IP做DNAT就能访问公司的重要数据。

NAT的初始化也从三个方面介绍

(1)NAT模块target注册

(2)NAT和四层协议相关结构体注册

(3)NAT的hook函数注册

1、NAT模块target注册

1.1、struct xt_target结构体

nat模块的taret函数就是实现nat的地址转换、端口转换的函数。struct xt_target实例定义格式如下

static struct xt_target ipt_snat_reg __read_mostly = {
	.name		= "SNAT",
	.target		= ipt_snat_target,	/*snat target处理函数*/
	.targetsize	= sizeof(struct nf_nat_multi_range_compat),
	.table		= "nat",
	.hooks		= 1 << NF_INET_POST_ROUTING,
	.checkentry	= ipt_snat_checkentry,
	.family		= AF_INET,
};

static struct xt_target ipt_dnat_reg __read_mostly = {
	.name		= "DNAT",
	.target		= ipt_dnat_target,	/*dnat target处理函数*/
	.targetsize	= sizeof(struct nf_nat_multi_range_compat),
	.table		= "nat",
	.hooks		= (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT),
	.checkentry	= ipt_dnat_checkentry,
	.family		= AF_INET,
};

1.2 struct xt_target结构体注册

nat的注册主要是将ipt_snat_reg和ipt_dnat_reg加入到全局变量static struct xt_af *xt;中,struct xt_af结构体定义,target初始化由nf_nat_rule_init函数完成。

xt全局数组:

static struct xt_af *xt;

nf_nat_rule_init:

int __init nf_nat_rule_init(void)
{
	int ret;

	ret = register_pernet_subsys(&nf_nat_rule_net_ops);
	if (ret != 0)
		goto out;
	/*初始化snat target*/
	ret = xt_register_target(&ipt_snat_reg);
	if (ret != 0)
		goto unregister_table;
	/*初始化dnat target*/
	ret = xt_register_target(&ipt_dnat_reg);
	if (ret != 0)
		goto unregister_snat;

	return ret;

 unregister_snat:
	xt_unregister_target(&ipt_snat_reg);
 unregister_table:
	unregister_pernet_subsys(&nf_nat_rule_net_ops);
 out:
	return ret;
}

xt_register_target将snat、dnat的实例加入到xt全局链表中。

int
xt_register_target(struct xt_target *target)
{
	u_int8_t af = target->family;
	int ret;

	ret = mutex_lock_interruptible(&xt[af].mutex);
	if (ret != 0)
		return ret;
	/*将target加入到xt全局数组中*/
	list_add(&target->list, &xt[af].target);
	mutex_unlock(&xt[af].mutex);
	return ret;
}

2、NAT和四层协议相关结构体注册

2.1 struct nf_nat_protocol结构体体

做NAT即包括网络层IP地址转换,也包括其他协议的特性转换,比如tcp/udp的端口转换,icmp协议id转换,不同协议有不同实例,nat和其他协议转换的函数定义在结构体struct nf_nat_protocol

struct nf_nat_protocol {
	/* Protocol number. */
	/*协议号*/
	unsigned int protonum;

	struct module *me;

	/* Translate a packet to the target according to manip type.
	   Return true if succeeded. */
	   /*对四层协议的做NAT转换*/
	bool (*manip_pkt)(struct sk_buff *skb,
			  unsigned int iphdroff,
			  const struct nf_conntrack_tuple *tuple,
			  enum nf_nat_manip_type maniptype);

	/* Is the manipable part of the tuple between min and max incl? */
	/*做NAT时判断协议的特性是否在合法范围内(TCP/UDP就是判断端口是否合法)*/
	bool (*in_range)(const struct nf_conntrack_tuple *tuple,
			 enum nf_nat_manip_type maniptype,
			 const union nf_conntrack_man_proto *min,
			 const union nf_conntrack_man_proto *max);

	/* Alter the per-proto part of the tuple (depending on
	   maniptype), to give a unique tuple in the given range if
	   possible; return false if not.  Per-protocol part of tuple
	   is initialized to the incoming packet. */
	   /*根据tuple和rang选择一个没有使用的tuple*/
	bool (*unique_tuple)(struct nf_conntrack_tuple *tuple,
			     const struct nf_nat_range *range,
			     enum nf_nat_manip_type maniptype,
			     const struct nf_conn *ct);

	int (*range_to_nlattr)(struct sk_buff *skb,
			       const struct nf_nat_range *range);

	int (*nlattr_to_range)(struct nlattr *tb[],
			       struct nf_nat_range *range);
};

tcp协议实例:

const struct nf_nat_protocol nf_nat_protocol_tcp = {
	.protonum		= IPPROTO_TCP,
	.me			= THIS_MODULE,
	/*对四层协议的做NAT转换*/
	.manip_pkt		= tcp_manip_pkt,
	/*做NAT时判断四层协议是否在合法范围内(TCP/UDP就是判断端口是否合法)*/
	.in_range		= nf_nat_proto_in_range,
	/*根据tuple和rang选择一个没有使用的tuple*/
	.unique_tuple		= tcp_unique_tuple,
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
	.range_to_nlattr	= nf_nat_proto_range_to_nlattr,
	.nlattr_to_range	= nf_nat_proto_nlattr_to_range,
#endif
};

udp协议实例:

const struct nf_nat_protocol nf_nat_protocol_udp = {
	.protonum		= IPPROTO_UDP,
	.me			= THIS_MODULE,
	.manip_pkt		= udp_manip_pkt,
	.in_range		= nf_nat_proto_in_range,
	.unique_tuple		= udp_unique_tuple,
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
	.range_to_nlattr	= nf_nat_proto_range_to_nlattr,
	.nlattr_to_range	= nf_nat_proto_nlattr_to_range,
#endif
};

icmp协议实例:

const struct nf_nat_protocol nf_nat_protocol_icmp = {
	.protonum		= IPPROTO_ICMP,
	.me			= THIS_MODULE,
	.manip_pkt		= icmp_manip_pkt,
	.in_range		= icmp_in_range,
	.unique_tuple		= icmp_unique_tuple,
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
	.range_to_nlattr	= nf_nat_proto_range_to_nlattr,
	.nlattr_to_range	= nf_nat_proto_nlattr_to_range,
#endif
};

2.2  struct nf_nat_protocol实例注册

struct nf_nat_protocol实例注册函数是nf_nat_init,起始就是把tcp/udp/icmp协议的struct nf_nat_protocol实例加入到nf_nat_protos全局数组中,做nat方便调用对用的函数。内核中很多地方都是定义一个结构体,结构体中有函数指针元素,然后将结构体实例加入到一个全局数组中。

static int __init nf_nat_init(void)
{
...
	/* Sew in builtin protocols. */
	spin_lock_bh(&nf_nat_lock);
	for (i = 0; i < MAX_IP_NAT_PROTO; i++)	
		rcu_assign_pointer(nf_nat_protos[i], &nf_nat_unknown_protocol);
	/*tcp实例加入到nf_nat_protos全局数组中*/
	rcu_assign_pointer(nf_nat_protos[IPPROTO_TCP], &nf_nat_protocol_tcp);
	rcu_assign_pointer(nf_nat_protos[IPPROTO_UDP], &nf_nat_protocol_udp);
	rcu_assign_pointer(nf_nat_protos[IPPROTO_ICMP], &nf_nat_protocol_icmp);
	spin_unlock_bh(&nf_nat_lock);
...

 cleanup_extend:
	nf_ct_extend_unregister(&nat_extend);
	return ret;
}

nf_nat_protos全局数组最大是256。

#define MAX_IP_NAT_PROTO 256
static const struct nf_nat_protocol *nf_nat_protos[MAX_IP_NAT_PROTO]
						__read_mostly;

3、hook注册

3.1、nf_nat_ops

NAT在PRE_ROUTEING、LOCAL_IN、POST_ROUTING、OUTING这个四个链注册了钩子函数,钩子函数详解下一节再讲

static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
	/* Before packet filtering, change destination */
	{
		/*做dnat*/
		.hook		= nf_nat_in,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_PRE_ROUTING,
		.priority	= NF_IP_PRI_NAT_DST,
	},
	/* After packet filtering, change source */
	{
		/*做snat*/
		.hook		= nf_nat_out,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_POST_ROUTING,
		.priority	= NF_IP_PRI_NAT_SRC,
	},
	/* Before packet filtering, change destination */
	{
		.hook		= nf_nat_local_fn,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_OUT,
		.priority	= NF_IP_PRI_NAT_DST,
	},
	/* After packet filtering, change source */
	{
		.hook		= nf_nat_fn,
		.owner		= THIS_MODULE,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_IN,
		.priority	= NF_IP_PRI_NAT_SRC,
	},
};

3.2 hook注册

hook注册调用nf_nat_standalone_init,最终加入到nf_hooks全局数组中

static int __init nf_nat_standalone_init(void)
{
	int ret = 0;

	need_ipv4_conntrack();

#ifdef CONFIG_XFRM
	BUG_ON(ip_nat_decode_session != NULL);
	rcu_assign_pointer(ip_nat_decode_session, nat_decode_session);
#endif
	ret = nf_nat_rule_init();
	if (ret < 0) {
		pr_err("nf_nat_init: can't setup rules.\n");
		goto cleanup_decode_session;
	}
	/*注册nat的hook*/
	ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
	if (ret < 0) {
		pr_err("nf_nat_init: can't register hooks.\n");
		goto cleanup_rule_init;
	}
	return ret;

 cleanup_rule_init:
	nf_nat_rule_cleanup();
 cleanup_decode_session:
#ifdef CONFIG_XFRM
	rcu_assign_pointer(ip_nat_decode_session, NULL);
	synchronize_net();
#endif
	return ret;
}

猜你喜欢

转载自blog.csdn.net/City_of_skey/article/details/84962903