Chinaunix首页 | 论坛 | 博客
  • 博客访问: 251041
  • 博文数量: 90
  • 博客积分: 2775
  • 博客等级: 少校
  • 技术积分: 645
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-13 17:04
文章分类

全部博文(90)

文章存档

2015年(30)

2013年(4)

2012年(4)

2011年(12)

2010年(4)

2009年(36)

我的朋友

分类: LINUX

2015-06-09 10:35:31

文件路径:/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是新建状态,并且conntrackNAT信息并没有设置,把报文送到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设置statusNAT标志位*/

    __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;

}

阅读(426) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~