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;
}