继续回到get_unique_tuple,接着in_range返回值为0(因为ip地址唯一),我们看看find_appropriate_src做了什么
很明显一开始net->ct.nat_bysource为null,所以这个函数也返回0.
关键在find_best_ips_proto
它根据源tuple信息和range参数,生产新的tuple(映射后的),然后nf_nat_used_tuple查询是否已有回应报文在hash链表上. 最后用l4proto->unique_tuple保证tuple 的唯一性.
总之get_unique_tuple函数主要工作就是生产新的映射后的tuple。
由于ip信息已经改变,所以new_tuple和curr_tuple肯定不一样. 之后新映射的tuple再逆转为repl_tuple(映射后)
调用nf_conntrack_alter_reply改变ct信息:
-
/* Alter reply tuple (maybe alter helper). This is for NAT, and is
-
implicitly racy: see __nf_conntrack_confirm */
-
void nf_conntrack_alter_reply(struct nf_conn *ct,
-
const struct nf_conntrack_tuple *newreply)
-
{
-
struct nf_conn_help *help = nfct_help(ct);
-
-
/* Should be unconfirmed, so not in hash table yet */
-
NF_CT_ASSERT(!nf_ct_is_confirmed(ct));
-
-
pr_debug("Altering reply tuple of %p to ", ct);
-
nf_ct_dump_tuple(newreply);
-
-
ct->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply;
-
if (ct->master || (help && !hlist_empty(&help->expectations)))
-
return;
-
-
rcu_read_lock();
-
__nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC);
-
rcu_read_unlock();
-
}
即它改变了 ct->tuplehash[IP_CT_DIR_REPLY].tuple,其他不变.
最后处理nat ext,把nat信息和ct关联起来通过hlist:net->ct.nat_bysource
然后设置ct->status:
-
ct->status |= IPS_SRC_NAT_DONE
既然nat规则处理完毕,剩下的工作就是处理skb里ip报文信息了。
根据ct里的信息通过nf_nat_packet修改skb指向的ip头.
后续的钩子函数,如果有helper处理helper;然后就是ipv4_confirmed
-
/* reuse the hash saved before */
-
hash = *(unsigned long *)&ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev;
-
hash = hash_bucket(hash, net);
-
repl_hash = hash_conntrack(net, zone,
-
&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
首先看第一个hash值,它在__nf_conntrack_alloc的时候被赋值,
Hash计算来自源五元组通过hash_conntrack_raw计算得来.那么snat的时候,基本ct处理后就是nat了,把ct里改变的是ct->tuplehash[IP_CT_DIR_REPLY].tuple信息,其他不变
这样的话 ipv4_confirm的时候,源hash不变,repl_hash重新计算(因为tuple的ip已经重新映射),然后加入inert ct的全局链表net->ct.hash[hash]
既然第一报文已经顺利发送出去,那么响应报文又是如何发给lan侧的呢?
首先202.20.65.5收到报文到prerouting钩子,nf_conntrack_in它会查询到ct信息
设置ctinfo 为IP_CT_ESTABLISHED_REPLY
然后看看ct timeout是否过期,然后调用l4proto->packet更新状态
Ct->status : confirmed + ips_src_nat
我们知道prerouting只能进行dnat。然后会进入dnat的钩子函数nf_nat_ipv4_in
根据ctinfo的信息,直接进入nf_nat_packet处理.
它里面有个关键部分:
-
/* Invert if this is reply dir. */
-
if (dir == IP_CT_DIR_REPLY)
-
statusbit ^= IPS_NAT_MASK;
然后进行dnat处理根据ct信息修改skb指向的ip头信息.即所谓的de-snat.同理dnat
简单看下五元组信息的变化:
我们可以看看数据伍元整的流程:
Lan-wan:
Ct(snat)即ct->tuplehash信息
orig:192.168.1.x----202.20.65.4
reply:202.20.65.4----202.20.65.5
回复的报文:
查询到ct(根据202.20.65.4----202.20.65.5)
Prerouting上nat处理. 由于这个时候只能处理dnat,
找到ct里源五元组即192.168.1.x----202.20.65.4反转为202.20.65.4---192.168.1.x
Skb根据这个信息进行dnat映射。即完成了正常的通信.
当然nat还有其他很多复杂的应用,这里仅仅分析一个实例应用的流程,作为深入理解nat的开始.