分类: LINUX
2017-08-15 15:45:19
文件路径:/net/ipv4/netfilter/nf_nat_standalone.c
static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
/* Before packet filtering, change destination */
{
.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 */
{
.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,
},
};
我们看,每个hook点上的处理函数最后都调用了nf_nat_fn这个函数。
static unsigned int nf_nat_fn(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
struct nf_conn_nat *nat;
/* maniptype == SRC for postrouting. */
enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
/* We never see fragments: conntrack defrags on pre-routing
and local-out, and nf_nat_out protects post-routing. */
/*NAT模块无法处理分片报文*/
NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)));
/*取得skb携带的conntrack*/
ct = nf_ct_get(skb, &ctinfo);
/* Can't track? It's not due to stress, or conntrack would
have dropped it. Hence it's the user's responsibilty to
packet filter it out, or implement conntrack/NAT for that
protocol. 8) --RR */
if (!ct)
return NF_ACCEPT;
/* Don't try to NAT if this packet is not conntracked */
if (ct == &nf_conntrack_untracked)
return NF_ACCEPT;
nat = nfct_nat(ct);
if (!nat) {
/* NAT module was loaded late. */
/*如果该conntrack是确认状态,并且没有nat扩展功能,就不需要处理NAT*/
if (nf_ct_is_confirmed(ct))
return NF_ACCEPT;
/*申请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;
}
}
switch (ctinfo) {
case IP_CT_RELATED:
case IP_CT_RELATED+IP_CT_IS_REPLY:
if (ip_hdr(skb)->protocol == IPPROTO_ICMP)
{
if (!nf_nat_icmp_reply_translation(ct, ctinfo, hooknum, skb))
return NF_DROP;
else
return NF_ACCEPT;
}
/* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
case IP_CT_NEW:
/* Seen it before? This can happen for loopback, retrans,
or local packets.. */
/*conntrack是新建状态,并且conntrack的NAT信息并没有设置,把报文送到nat表中进行查找处理*/
if (!nf_nat_initialized(ct, maniptype))
{
unsigned int ret;
if (hooknum == NF_INET_LOCAL_IN)
/* LOCAL_IN hook doesn't have a chain! */
/*本机发送的报文不进行NAT的处理,给conntrack设置一个不进行
NAT转换的NAT信息,防止该连接上的后续报文每次都进入NAT做一些不必要
的操作(因为nf_nat_initialized函数返回为0)*/
ret = alloc_null_binding(ct, hooknum);
else
ret = nf_nat_rule_find(skb, hooknum, in, out, ct);
if (ret != NF_ACCEPT) {
return ret;
}
} else
pr_debug("Already setup manip %s for ct %p\n",
maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",ct);
break;
/*如果该conntrack是已建立连接的状态,就无需查找nat表,直接根据conntrack里的
信息进行NAT转换即可*/
default:
/* ESTABLISHED */
NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY));
}
/*根据conntrack中的NAT转换信息对报文进行NAT处理*/
return nf_nat_packet(ct, ctinfo, hooknum, skb);
}
在NAT表中处理报文
int nf_nat_rule_find(struct sk_buff *skb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
struct nf_conn *ct)
{
struct net *net = nf_ct_net(ct);
int ret;
/*查找NAT表并进行报文的处理,会调用NAT target的处理函数对
conntrack进行设置*/
ret = ipt_do_table(skb, hooknum, in, out, net->ipv4.nat_table);
if (ret == NF_ACCEPT)
{
/*如果NAT表中没有配置匹配该报文的NAT规则,,就根据报文的ip地址设置一
个不进行NAT转换的NAT规则,这样做的目的是避免该连接上的后续报文都进行
NAT表的查询匹配操作。*/
if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum)))
/* NUL mapping */
ret = alloc_null_binding(ct, hooknum);
}
return ret;
}
设置一个不进行NAT转换的NAT设置
unsigned int alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
{
/*该函数只是调用nf_nat_setup_info函数
给conntrack设置status的NAT标志位*/
__be32 ip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
? ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip
: ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip);
struct nf_nat_range range = { IP_NAT_RANGE_MAP_IPS, ip, ip, { 0 }, { 0 } };
pr_debug("Allocating NULL binding for %p (%pI4)\n", ct, &ip);
return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
}
经过以上的处理,skb->nf_cn所带的conntrack信息中已经配置好了进行NAT转换的所有信息。这时我们该调用nf_nat_packet函数进行报文的IP地址和端口号的替换。
unsigned int nf_nat_packet(struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
unsigned int hooknum,
struct sk_buff *skb)
{
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
unsigned long statusbit;
/*根据HOOK点来得出是进行SNAT还是DNAT操作*/
enum nf_nat_manip_type mtype = HOOK2MANIP(hooknum);
if (mtype == IP_NAT_MANIP_SRC)
statusbit = IPS_SRC_NAT;
else
statusbit = IPS_DST_NAT;
/* Invert if this is reply dir. */
/*如果是回应方向上的报文,应该做相反的还原NAT操作*/
if (dir == IP_CT_DIR_REPLY)
statusbit ^= IPS_NAT_MASK;
/* Non-atomic: these bits don't change. */
/*判断该hook点是否需要进行对应的NAT的操作,如果不需要,就放行报文*/
if (ct->status & statusbit)
{
struct nf_conntrack_tuple target;
/* We are aiming to look like inverse of other direction. */
/*如果需要进行NAT操作,就是要反方向上的tuple对报文进行NAT的转换*/
nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
/*修改报文的IP地址和端口信息,替换后重新计算报文头的校验和*/
if (!manip_pkt(target.dst.protonum, skb, 0, &target, mtype))
return NF_DROP;
}
return NF_ACCEPT;
}