SNAT、DNAT目標函數
前面在ip_nat_fn()函數中調用的ip_nat_rule_find()用來查找NAT規則,執行規則的動作,規則目標不是SNAT就是DNAT,該目標的具體實現在net/ipv4/netfilter/ip_nat_rule.c中。
不論是SNAT還是DNAT規則,其目標函數最終都是調用ip_nat_setup_info()函數來建立連接的NAT info信息。
net/ipv4/netfilter/ip_nat_rule.c:
/* Source NAT */
static unsigned int ipt_snat_target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
const void *targinfo,
void *userinfo)
{
struct ip_conntrack *ct;
enum ip_conntrack_info ctinfo;
IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING);
ct = ip_conntrack_get(*pskb, &ctinfo);
/* Connection must be valid and new. */
IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
IP_NF_ASSERT(out);
// 只有新連接才進行NAT info的建立
// targinfo實際是struct ip_nat_multi_range結構指針,記錄轉換後的
// 地址、端口等信息, 一個NAT規則可以轉換到可以轉換到多個地址端口上
return ip_nat_setup_info(ct, targinfo, hooknum);
}
static unsigned int ipt_dnat_target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
const void *targinfo,
void *userinfo)
{
struct ip_conntrack *ct;
enum ip_conntrack_info ctinfo;
#ifdef CONFIG_IP_NF_NAT_LOCAL
IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
|| hooknum == NF_IP_LOCAL_OUT);
#else
IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING);
#endif
ct = ip_conntrack_get(*pskb, &ctinfo);
/* Connection must be valid and new. */
IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
// 只有新連接才進行NAT info的建立
// targinfo實際是struct ip_nat_multi_range結構指針,記錄轉換後的
// 地址、端口等信息, 一個NAT規則可以轉換到可以轉換到多個地址端口上
return ip_nat_setup_info(ct, targinfo, hooknum);
}
......
int ip_nat_rule_find(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
struct ip_conntrack *ct,
struct ip_nat_info *info)
{
int ret;
ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL);
if (ret == NF_ACCEPT) {
// 數據接受但有沒有初始化,分配一個NULL binding,實際不作任何修改,也就是
// 說對該包沒有相應的NAT規則對於,不需要進行NAT處理
if (!(info->initialized & (1 nat.info;
// 如果info->initialized不為0,表示已經初始化過了
int in_hashes = info->initialized;
MUST_BE_WRITE_LOCKED(&ip_nat_lock);
IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
|| hooknum == NF_IP_POST_ROUTING
|| hooknum == NF_IP_LOCAL_OUT);
IP_NF_ASSERT(info->num_manips initialized & (1 tuplehash[IP_CT_DIR_ORIGINAL].tuple) */
// 根據連接的回應方向的tuple進行反轉得到原始方向的tuple
invert_tuplepr(&orig_tp,
&conntrack->tuplehash[IP_CT_DIR_REPLY].tuple);
#if 0
{
unsigned int i;
DEBUGP("Hook %u (%s), ", hooknum,
HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST");
DUMP_TUPLE(&orig_tp);
DEBUGP("Range %p: ", mr);
for (i = 0; i rangesize; i++) {
DEBUGP("%u:%s%s%s %u.%u.%u.%u - %u.%u.%u.%u %u - %u\n",
i,
(mr->range.flags & IP_NAT_RANGE_MAP_IPS)
? " MAP_IPS" : "",
(mr->range.flags
& IP_NAT_RANGE_PROTO_SPECIFIED)
? " PROTO_SPECIFIED" : "",
(mr->range.flags & IP_NAT_RANGE_FULL)
? " FULL" : "",
NIPQUAD(mr->range.min_ip),
NIPQUAD(mr->range.max_ip),
mr->range.min.all,
mr->range.max.all);
}
}
#endif
do {
// 找一個未使用的進行了轉換後的tuple結構參數,mr是NAT規則確定的要轉換後的
// 地址端口參數, new_tuple保持轉換後的連接原始方向的tuple
if (!get_unique_tuple(&new_tuple, &orig_tp, mr, conntrack,
hooknum)) {
DEBUGP("ip_nat_setup_info: Can't get unique for %p.\n",
conntrack);
return NF_DROP;
}
#if 0
DEBUGP("Hook %u (%s) %p\n", hooknum,
HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST",
conntrack);
DEBUGP("Original: ");
DUMP_TUPLE(&orig_tp);
DEBUGP("New: ");
DUMP_TUPLE(&new_tuple);
#endif
/* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT):
本文URL地址:http://www.bianceng.cn/OS/Linux/201410/45494.htm
the original (A/B/C/D') and the mangled one (E/F/G/H').
We're only allowed to work with the SRC per-proto
part, so we create inverses of both to start, then
derive the other fields we need. */
/* Reply connection: simply invert the new tuple
(G/H/E/F') */
// 建立連接地址轉換後的反向的tuple,這使netfilter能自動對連接的反方向數據
// 進行處理,也就是說定義了一條SNAT規則後,並不需要再定義一條DNAT規則來處理
// 返回的數據,netfilter已經自動處理了
invert_tuplepr(&reply, &new_tuple);
/* Alter conntrack table so it recognizes replies.
If fail this race (reply tuple now used), repeat. */
// 修改連接參數使能正確識別返回數據,如果reply已經對應一條連接
// ip_conntrack_alter_reply()函數返回0,表示要繼續修改轉換後的參數值
} while (!ip_conntrack_alter_reply(conntrack, &reply));
/* FIXME: We can simply used existing conntrack reply tuple
here --RR */
/* Create inverse of original: C/D/A/B' */
invert_tuplepr(&inv_tuple, &orig_tp);
/* Has source changed?. */
// 源NAT
if (!ip_ct_tuple_src_equal(&new_tuple, &orig_tp)) {
/* In this direction, a source manip. */
// 連接正方向是SNAT
info->manips[info->num_manips++] =
((struct ip_nat_info_manip)
{ IP_CT_DIR_ORIGINAL, hooknum,
IP_NAT_MANIP_SRC, new_tuple.src });
IP_NF_ASSERT(info->num_manips manips[info->num_manips++] =
((struct ip_nat_info_manip)
{ IP_CT_DIR_REPLY, opposite_hook[hooknum],
IP_NAT_MANIP_DST, orig_tp.src });
IP_NF_ASSERT(info->num_manips manips[info->num_manips++] =
((struct ip_nat_info_manip)
{ IP_CT_DIR_ORIGINAL, hooknum,
IP_NAT_MANIP_DST, reply.src });
IP_NF_ASSERT(info->num_manips manips[info->num_manips++] =
((struct ip_nat_info_manip)
{ IP_CT_DIR_REPLY, opposite_hook[hooknum],
IP_NAT_MANIP_SRC, inv_tuple.src });
IP_NF_ASSERT(info->num_manips master)
info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,
&reply);
/* It's done. */
// 完成該方向的NAT info初始化
info->initialized |= (1 bysource.conntrack);
replace_in_hashes(conntrack, info);
} else {
place_in_hashes(conntrack, info);
}
return NF_ACCEPT;
}