内核netfilter是通过iptables进行进行过滤规则设置的,因此snat方可通过IPtables工具设置一定的规则来实现。为了得到snat的工作原理,这里介绍一下它的流程:
在挂钩点NF_IP_POST_ROUTING安装上钩子(?),首先将Packet钩住,而后调用nf_nat_rule_find来匹配iptables工具在内核设置的规则链。而具体的匹配算法是在ipt_do_table函数中实现的。Ip_do_table函数流程如下:
开始:ipt_do_table(...)
|
V
ip=ip_hdr(skb);//获得Packet的ip头部
|
V
判断此Packet是进入网卡还是出网卡的?
|
V
设置循环匹配条件
|
V
read_lock_bh(...)//获得自旋锁,以便锁住随后的临界区
|
V
e = get_entry(table_base,private->hook_entry[hook]);//获得规则连入口
back = get_entry(table_base,private->underflow[hook]);//获得下一规则
|
V
do{ //进入do-while循环,开始正式的规则匹配
|
V
ip_packet_match()
IPT_MATCH_ITERATE()//规则匹配
|
V
ADD_COUNTER()// 增加规则计数器
ipt_get_target()//获得target
get_entry()//
e=back;//保存规则链指针
back=next;//
|
V
no_match:e=(void *)e+e->next_offset;
}while(...);
|
V
结束
以上的规则匹配工作若成功,则会进入SNAT或DNAT实质性阶段,函数调用过程如下:首先,NF_HOOK_COND()->nf_hook_thresh()->nf_hook_slow->nf_iterate()->struct nf_hook_ops{nf_nat_in,nf_nat_out,nf_nat_local_fn,nf_nat_fn}->nf_nat_packet()
->nf_ct_invert_tuple()->nf_ct_invert_tuple()->l4proto_invert_tuple()->.invert_tuple =
ipv4_invert_tuple->ipv4_invert_tuple 至此已找到了源IP被替换的代码。ipv4_invert_tuple()
函数的实现代码如下:
static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_tuple *orig)
{
tuple->src.u3.ip = orig->dst.u3.ip;
tuple->dst.u3.ip = orig->src.u3.ip;
return true;
}
此函数可以用于SNAT/DNAT。原因是在发包时,需将packet中的源IP更改,恰对应第一条语句,第二条语句用于保存规则链中的相应源IP。这时会出现一个问题:若是源IP为any,该怎么办?别着急!这时就要看tcp_invert_tuple函数中设置的port了。收包时,原理同上。