2018年(21)
分类: LINUX
2018-09-19 15:49:31
文件路径:/net/ipv4/netfilter/nf_nat_rule.c
#define NAT_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \
(1 << NF_INET_POST_ROUTING) | \
(1 << NF_INET_LOCAL_OUT))
static const struct xt_table nat_table = {
.name = "nat",
.valid_hooks = NAT_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_IPV4,
};
static int __net_init nf_nat_rule_net_init(struct net *net)
{
/*注册nat表到Netfilter中*/
net->ipv4.nat_table = ipt_register_table(net, &nat_table,
&nat_initial_table.repl);
if (IS_ERR(net->ipv4.nat_table))
return PTR_ERR(net->ipv4.nat_table);
return 0;
}
static struct xt_target ipt_snat_reg __read_mostly = {
.name = "SNAT",
.target = ipt_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,
.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,
};
int __init nf_nat_rule_init(void)
{
int ret;
/*注册扩展TARGET*/
ret = xt_register_target(&ipt_snat_reg);
ret = xt_register_target(&ipt_dnat_reg);
return ret;
}
扩展target的参数定义
struct nf_nat_multi_range_compat
{
unsigned int rangesize; /* Must be 1. */
/* hangs off end. */
struct nf_nat_range range[1];
};
struct nf_nat_range
{
/* Set to OR of flags above. */
unsigned int flags;
/* Inclusive: network order. */
/*映射的最大和最小IP地址*/
__be32 min_ip, max_ip;
/* Inclusive: network order */
/*映射的最大和最小端口号*/
union nf_conntrack_man_proto min, max;
};
SNAT target的处理函数
static unsigned int ipt_snat_target(struct sk_buff *skb, const struct xt_target_param *par)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
/*取得用户配置的TARGET参数*/
const struct nf_nat_multi_range_compat *mr = par->targinfo;
/*SNAT target必须配置在POSTROUTING HOOK点上*/
NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING);
/*取得报文携带的conntrack信息及conntrack的状态*/
ct = nf_ct_get(skb, &ctinfo);
/* Connection must be valid and new. */
/*conntrack 的状态必须是以下几种双方未建立连接的状态,否则就报错。
这是因为连接已建立后,报文就直接根据conntrack进行处理NAT即可,
不会再查nat使用SNAT target进行NAT的处理了*/
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY));
/*在进行SNAT前,报文必须先经过路由找到出接口,否则报错*/
NF_CT_ASSERT(par->out != NULL);
/*把用户配置的映射范围传给 nf_nat_setup_info进行进一步处理*/
return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_SRC);
}
DNAT target的处理函数
static unsigned int ipt_dnat_target(struct sk_buff *skb, const struct xt_target_param *par)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
const struct nf_nat_multi_range_compat *mr = par->targinfo;
/*DNAT target必须配置在 PREROUTING及OUTPUT两个hook点上*/
NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING ||
par->hooknum == NF_INET_LOCAL_OUT);
/*取得报文携带的conntrack信息及conntrack的状态*/
ct = nf_ct_get(skb, &ctinfo);
/* Connection must be valid and new. */
/*查nat表进行DNAT target处理时的报文连接状态必须是如下几种未建立连接的
状态*/
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
/*把用户配置的映射范围传给 nf_nat_setup_info进行进一步处理*/
return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_DST);
}
NAT target处理的核心函数
unsigned int nf_nat_setup_info(struct nf_conn *ct,
const struct nf_nat_range *range,
enum nf_nat_manip_type maniptype)
{
struct net *net = nf_ct_net(ct);
struct nf_conntrack_tuple curr_tuple, new_tuple;
struct nf_conn_nat *nat;
int have_to_hash = !(ct->status & IPS_NAT_DONE_MASK);
/* nat helper or nfctnetlink also setup binding */
/*如果该conntrack没有预留NAT功能使用的扩展空间,就添加扩展NAT功能
的扩展空间*/
nat = nfct_nat(ct);
if (!nat) {
nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
if (nat == NULL) {
pr_debug("failed to add NAT extension\n");
return NF_ACCEPT;
}
}
NF_CT_ASSERT(maniptype == IP_NAT_MANIP_SRC ||
maniptype == IP_NAT_MANIP_DST);
/*检查conntrack是否已经被NAT target处理过,如果处理过就报错*/
BUG_ON(nf_nat_initialized(ct, maniptype));
/*根据conntrack的reply方向的tuple信息,同时根据target的配置生成经过
NAT转换后的tupel信息,并替换原来reply的tuple信息*/
nf_ct_invert_tuplepr(&curr_tuple,
&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
/*根据target的配置获取一个合适NAT转换之后的IP地址和端口信息*/
get_unique_tuple(&new_tuple, &curr_tuple, range, ct, maniptype);
if (!nf_ct_tuple_equal(&new_tuple, &curr_tuple)) {
struct nf_conntrack_tuple reply;
/* Alter conntrack table so will recognize replies. */
/*更新reply tuple信息*/
nf_ct_invert_tuplepr(&reply, &new_tuple);
nf_conntrack_alter_reply(ct, &reply);
/* Non-atomic: we own this at the moment. */
/*设置conntrack的status NAT位*/
if (maniptype == IP_NAT_MANIP_SRC)
ct->status |= IPS_SRC_NAT;
else
ct->status |= IPS_DST_NAT;
}
/* Place in source hash if this is the first time. */
/*把该conntrack 的original tuple加入到bysource hash表中,bysource hash表是
查找是否已经映射过得报文使用,即端口和地址映射表*/
if (have_to_hash) {
unsigned int srchash;
srchash = hash_by_src(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
spin_lock_bh(&nf_nat_lock);
/* nf_conntrack_alter_reply might re-allocate exntension aera */
nat = nfct_nat(ct);
nat->ct = ct;
hlist_add_head_rcu(&nat->bysource,&net->ipv4.nat_bysource[srchash]);
spin_unlock_bh(&nf_nat_lock);
}
/* It's done. */
/*设置NAT_DONE位,表明该conntrack中的NAT信息已经更新完毕,以后的报文只
需要使用conntrack带的NAT信息处理报文即可,不需要在使用查NAT来处理了*/
if (maniptype == IP_NAT_MANIP_DST)
set_bit(IPS_DST_NAT_DONE_BIT, &ct->status);
else
set_bit(IPS_SRC_NAT_DONE_BIT, &ct->status);
return NF_ACCEPT;
}
EXPORT_SYMBOL(nf_nat_setup_info);
从上面我们看到,NAT的target处理函数只是更新的conntrack中的信息,并没有修改报文。报文的修改放在了NAT的hook函数里进行处理。
(未完待续)