netfilter之NAT代码解读


      地址转换用来改变源/目的地址/端口,是netfilter的一部分,也是通过hook点上注册相应的结构来工作 

      Nat注册的hook点和conntrack相同,只是优先级不同,数据包进入netfilter之后先经过conntrack,再经过nat。而在数据包离开netfilter之前先经过nat,再经过conntrack

 

1  nat模块的初始化

1.1      数据结构    ip_nat_standalone.c

ip_conntrack结构中有为nat定义的一个nat结构,为什么把这个结构放在ip_conntrack里呢。简单的说,对于非初始化连接的数据包,即后续的数据包,一旦确定它属于某个连接,则可以直接利用连接状态里的nat信息来进行地址转换;而对于初始数据包,必须在nat表里查找相应的规则,确定了地址转换的内容后,将这些信息放到连接跟踪结构的nat参量里面,供后续的数据包使用。

#ifdef CONFIG_IP_NF_NAT_NEEDED

     struct {

         struct ip_nat_info info;

         union ip_conntrack_nat_help help;

#ifdefined(CONFIG_IP_NF_TARGET_MASQUERADE) || \

     defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)

         intmasq_index;

#endif

#if defined(CONFIG_IP_NF_RTSP) ||defined(CONFIG_IP_NF_RTSP_MODULE)

                struct ip_nat_rtsp_infortsp_info;

#endif

     } nat;

#endif /* CONFIG_IP_NF_NAT_NEEDED*/

 

#ifdefined(CONFIG_IP_NF_CONNTRACK_MARK)

     unsignedlong mark;

#endif

 

 

它包括两个参数,struct ip_nat_infounionip_conntrack_nat_help,后一个暂时没什么用,只看前一个

struct ip_nat_info

{

     /* 用来检测该连接是否已经进行过某类nat初始化了,在新的内核中该参数被去掉了,当然,有其它方法来实现它的作用。 */

     intinitialized;

    

     unsignedint num_manips;

 

     /* 这个就是用来存储关于如何进行地址转换的相关信息的数据结构,其中IP_NAT_MAX_MANIPS代表某个连接的数据包在经过netfilter一次的过程中最多能进行的地址转换的次数,这里是(2*3=6 。意思大概是说对于某个连接,如果nat表的每条链上都有一条规则:

NF_IP_PRE_ROUTING==>NF_IP_POST_ROUTING
如果在NF_IP_PRE_ROUTING上做目的转换,要在NF_IP_POST_ROUTING上做反方向上的源转换
NF_IP_POST_ROUTING==>NF_IP_PRE_ROUTING
如果在NF_IP_POST_ROUTING上做源转换,要在NF_IP_PRE_ROUTING上做反方向上的目的转换
NF_IP_LOCAL_OUT==>NF_IP_LOCAL_IN
如果在NF_IP_LOCAL_OUT做源转换,要在NF_IP_LOCAL_IN上做反方向上的目的转换 

算下来就是最多进行6次地址转换 */

     struct ip_nat_info_manip manips[IP_NAT_MAX_MANIPS];

 

     /* 两个全局hash表,用来将所有需要进行地址转换的连接组织起来 */

     structip_nat_hash bysource, byipsproto;

 

     /* 做特殊用途,通常是NULL */

     structip_nat_helper *helper;

 

     structip_nat_seq seq[IP_CT_DIR_MAX];

};

 

ip_nat_info_manip结构定义如下:

struct ip_nat_info_manip

{

     /* 方向,初始或应答 */

     u_int8_tdirection;

 

     /* 转换发生的hook */

     u_int8_thooknum;

 

     /* 转换的类型,源还是目的 */

     u_int8_tmaniptype;

 

     /* Manipulations to occur at each conntrack in this dirn. */

     structip_conntrack_manip manip;

};

 

struct ip_conntrack_manip

{

     u_int32_tip;

     unionip_conntrack_manip_proto u;

};

 

 

ip_nat_hash结构   ip_nat.h

struct ip_nat_hash

{

     structlist_head list;

     structip_conntrack *conntrack;

};

 

 

1.2      init()函数    ip_nat_standalone.c

static int __init init(void)

{

     returninit_or_cleanup(1);

}

init()函数直接调用init_or_cleanup()

 

static int init_or_cleanup(intinit)

{

     intret = 0;

/* nat依赖于conntrack,这个函数是空的 */

     need_ip_conntrack();

 

     if(!init) goto cleanup;

/* 初始化nat规则 */

     ret = ip_nat_rule_init();

     if(ret < 0) {

         printk("ip_nat_init:can't setup rules.\n");

         gotocleanup_nothing;

     }

/* 初始化nat */

     ret = ip_nat_init();

     if(ret < 0) {

         printk("ip_nat_init:can't setup rules.\n");

         gotocleanup_rule_init;

     }

/* 注册hook,共在四个hook点上注册了函数,分别是:

NF_IP_PRE_ROUTING   ip_nat_fn

NF_IP_POST_ROUTING  ip_nat_out

NF_IP_LOCAL_OUT   ip_nat_local_fn

NF_IP_LOCAL_IN     ip_nat_fn

NF_IP_LOCAL_OUTNF_IP_LOCAL_IN需要定义CONFIG_IP_NF_NAT_LOCAL

其中在ip_nat_outip_nat_local_fn中都会调用ip_nat_fn

*/

     ret= nf_register_hook(&ip_nat_in_ops);

     if(ret < 0) {

         printk("ip_nat_init:can't register in hook.\n");

         gotocleanup_nat;

     }

     ret= nf_register_hook(&ip_nat_out_ops);

     if(ret < 0) {

         printk("ip_nat_init:can't register out hook.\n");

         gotocleanup_inops;

     }

#ifdef CONFIG_IP_NF_NAT_LOCAL

     ret= nf_register_hook(&ip_nat_local_out_ops);

     if(ret < 0) {

         printk("ip_nat_init:can't register local out hook.\n");

         gotocleanup_outops;

     }

     ret= nf_register_hook(&ip_nat_local_in_ops);

     if(ret < 0) {

         printk("ip_nat_init:can't register local in hook.\n");

         gotocleanup_localoutops;

     }

#endif

     returnret;

 

 cleanup:

#ifdef CONFIG_IP_NF_NAT_LOCAL

     nf_unregister_hook(&ip_nat_local_in_ops);

 cleanup_localoutops:

     nf_unregister_hook(&ip_nat_local_out_ops);

 cleanup_outops:

#endif

     nf_unregister_hook(&ip_nat_out_ops);

 cleanup_inops:

     nf_unregister_hook(&ip_nat_in_ops);

 cleanup_nat:

     ip_nat_cleanup();

 cleanup_rule_init:

     ip_nat_rule_cleanup();

 cleanup_nothing:

     MUST_BE_READ_WRITE_UNLOCKED(&ip_nat_lock);

     returnret;

}

 

 

1.3  ip_nat_rule_init()函数  ip_nat_rule.c

int __init ip_nat_rule_init(void)

{

     intret;

/* 注册nat */

     ret = ipt_register_table(&nat_table);

     if(ret != 0)

         returnret;

/* 注册了两个target,一个是snat一个是dnat  */

     ret = ipt_register_target(&ipt_snat_reg);

     if(ret != 0)

         gotounregister_table;

     ret =ipt_register_target(&ipt_dnat_reg);

     if(ret != 0)

         gotounregister_snat;

 

     returnret;

 unregister_snat:

     ipt_unregister_target(&ipt_snat_reg);

 unregister_table:

     ipt_unregister_table(&nat_table);

 

     returnret;

}

 

看一下nat表的初始化:

static struct ipt_table nat_table= {

     .name     = "nat",

     .table      =&nat_initial_table.repl,

     .valid_hooks  = NAT_VALID_HOOKS,

     .lock        = RW_LOCK_UNLOCKED,

     .me         = THIS_MODULE,

};

filter表的初始化类似,一开始规则都是空的

 

两个target的初始化:

static struct ipt_targetipt_snat_reg = {

     .name     = "SNAT",

     .target          =ipt_snat_target,

     .checkentry   = ipt_snat_checkentry,

};

 

static struct ipt_targetipt_dnat_reg = {

     .name     = "DNAT",

     .target          =ipt_dnat_target,

     .checkentry   = ipt_dnat_checkentry,

};

两个target函数分别是ipt_snat_targetipt_dnat_target

 

 

1.4  ip_nat_init()函数  ipt_nat_core.c

int __init ip_nat_init(void)

{

     size_ti;

 

     /* nathash表大小和conntrackhash表相同 */

     ip_nat_htable_size= ip_conntrack_htable_size;

 

     /* 初始化了一个叫bysource的全局链表指针 */

     bysource= vmalloc(sizeof(struct list_head) * ip_nat_htable_size*2);

     if(!bysource) {

         return-ENOMEM;

     }

     /* 全局链表指针byipsproto,在bysource之后。bysourcebyipsproto实际上也是两个hash表,每个节点是一个ip_nat_hash结构,包含一个list_head和一个ip_conntrack。有点特别的就是nat用两个hash表来组织地址转换的数据结构,其本质是一样的,只是所使用的hash算法不同,bysource一般用于SNAT的处理,计算bysourcehash值的函数是hash_by_src()byipsproto用于DNAT的处理,计算byipsprotohash值的函数是hash_by_ipsproto()*/

     byipsproto= bysource + ip_nat_htable_size;

 

     /* 注册一些内建的协议,&protos是用来维护nat模块中用到的协议结构ip_nat_protocol的全局链表 */

     WRITE_LOCK(&ip_nat_lock);

     list_append(&protos,&ip_nat_protocol_tcp);

     list_append(&protos,&ip_nat_protocol_udp);

     list_append(&protos,&ip_nat_protocol_icmp);

     WRITE_UNLOCK(&ip_nat_lock);

 

     for(i = 0; i < ip_nat_htable_size; i++) {

/* 初始化bysourcebyipsproto中的所有链表,两个数组的大小都是ip_nat_htables_size,数组的每个节点是一个链表头 */

         INIT_LIST_HEAD(&bysource[i]);

         INIT_LIST_HEAD(&byipsproto[i]);

     }

 

     IP_NF_ASSERT(ip_conntrack_destroyed== NULL);

/* 初始化一个ip_conntrack_destroyed函数,ip_nat_cleanup_conntrack(struct ip_conntrack*conn) 的作用是在bysourcebyipproto链表中删除conn对应的节点 */

     ip_conntrack_destroyed= &ip_nat_cleanup_conntrack;

    

     /*Initialize fake conntrack so that NAT will skip it */

     ip_conntrack_untracked.nat.info.initialized|=

         (1<< IP_NAT_MANIP_SRC) | (1 << IP_NAT_MANIP_DST);

 

     return0;

}

 

 

地址转换的过程

2.1  ip_nat_fn函数  ip_nat_standalone.c

ip_nat_fn()nat中的主要函数,natnetfilter中注册了四个hook,最终都会调用该函数

static unsigned int

ip_nat_fn(unsigned int hooknum,

       struct sk_buff **pskb,

       const struct net_device *in,

       const struct net_device *out,

       int (*okfn)(struct sk_buff *))

{

     structip_conntrack *ct;

     enumip_conntrack_info ctinfo;

     structip_nat_info *info;

       /* 根据所在的hook点判断转换类型是源地址转换还是目的地址转换,为0(IP_NAT_MANIP_SRC)表示源地址转换,为1(IP_NAT_MANIP_DST)表示目的地址转换 */

     enumip_nat_manip_type maniptype = HOOK2MANIP(hooknum);

 

     /* 前面函数中已经处理过分片的情况,这里应该不会再出现分片包了. */

     IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off

                & htons(IP_MF|IP_OFFSET)));

 

     /*因为地址转换会修改数据包,所以这里先初始化将其设置为未修改标志,后面进行数据包修改时再来重置这个标志*/

     (*pskb)->nfcache|= NFC_UNKNOWN;

 

     /* 校验和 */

     if((*pskb)->ip_summed == CHECKSUM_HW)

         if(skb_checksum_help(pskb, (out == NULL)))

              returnNF_DROP;

/*取得数据包的连接状态*/

     ct= ip_conntrack_get(*pskb, &ctinfo);

     /* 如果找不到对应连接,则应该直接放行它,而不再对其进行转换处理,特别地,ICMP重定向报文将会被丢弃*/

     if(!ct) {

         /*Exception: ICMP redirect to new connection (not in

                   hash table yet).  We must not let this through, in

                   case we're doing NAT to thesame network. */

         if((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {

              structicmphdr hdr;

 

              if(skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4,

                         &hdr, sizeof(hdr)) == 0

                 && hdr.type == ICMP_REDIRECT)

                   returnNF_DROP;

         }

         returnNF_ACCEPT;

     }

/* 判断连接状态,调用相应的处理函数*/

     switch(ctinfo) {

     caseIP_CT_RELATED:

     caseIP_CT_RELATED+IP_CT_IS_REPLY:

         if((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {

              if(!icmp_reply_translation(pskb, ct, hooknum,

                                CTINFO2DIR(ctinfo)))

                   returnNF_DROP;

              else

                   return NF_ACCEPT;

         }

         /*Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */

/* 如果是一个初始连接的数据包 */

     caseIP_CT_NEW:

         info= &ct->nat.info;

 

         WRITE_LOCK(&ip_nat_lock);

/* 观察这个连接中的nat部分是否已经被初始化过了,如果有则跳过下面的部分,直接进行地址转换,如果没有,进一步判断 */  

     if(!(info->initialized & (1 << maniptype))

#ifndef CONFIG_IP_NF_NAT_LOCAL

             && !(ct->status &IPS_CONFIRMED)

#endif

             ) {

              unsignedint ret;

     /* 如果该连接是由expect创建的,并且有expect函数,则在这里调用 */

              if(ct->master

                  &&master_ct(ct)->nat.info.helper

                  && master_ct(ct)->nat.info.helper->expect){

                   ret= call_expect(master_ct(ct), pskb,

                              hooknum, ct, info);

              }else {

#ifdef CONFIG_IP_NF_NAT_LOCAL

                   /*LOCAL_IN hook doesn't have a chain!  */

                   if(hooknum == NF_IP_LOCAL_IN)

                       ret= alloc_null_binding(ct, info,

                                      hooknum);

                   else

#endif

     /* 既没有被nat修改过,也不是由expect创建,这是一个初始的数据包,开始在nat表中查找规则 */

              ret = ip_nat_rule_find(pskb,hooknum, in, out, ct, info);

              }

 

              if(ret != NF_ACCEPT) {

                   WRITE_UNLOCK(&ip_nat_lock);

                   returnret;

              }

         }else

/* 如果该连接的nat部分已经被初始化了,打印调试信息 */

              DEBUGP("Alreadysetup manip %s for ct %p\n",

                     maniptype == IP_NAT_MANIP_SRC ?"SRC" : "DST",

                     ct);

         WRITE_UNLOCK(&ip_nat_lock);

         break;

 

     default:

         /*ESTABLISHED */

         IP_NF_ASSERT(ctinfo== IP_CT_ESTABLISHED

                   || ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY));

         info= &ct->nat.info;

     }

 

     IP_NF_ASSERT(info);

     /* 前面已经修改了连接跟踪表,这里正式修改了数据包里的地址 */

     return do_bindings(ct, ctinfo, info, hooknum, pskb);

}

 

 

2.2          ip_nat_rule_find函数  ip_nat_rule.c

int ip_nat_rule_find(structsk_buff **pskb,

              unsigned int hooknum,

              const struct net_device *in,

              const struct net_device *out,

              struct ip_conntrack *ct,

              struct ip_nat_info *info)

{

     intret;

/* 调用ipt_do_tables函数,第五个参数是&nat_table  */

     ret = ipt_do_table(pskb, hooknum, in, out,&nat_table, NULL);

 

     if(ret == NF_ACCEPT) {

         if(!(info->initialized & (1 << HOOK2MANIP(hooknum))))

              /*NUL mapping */

              ret= alloc_null_binding(ct, info, hooknum);

     }

     returnret;

}

nat表和filter表一样,都是通过调用ipt_do_table函数来工作的

ipt_do_table查找表中的所有entry,如果match全都匹配,则调用target函数

此时的target函数就是在nat初始化时注册的ipt_snat_targetipt_dnat_target

 

 

2.3          ipt_sdnat_target函数  ip_nat_rule.c

static unsigned intipt_snat_target(struct sk_buff **pskb,

                       const struct net_device *in,

                       const struct net_device *out,

                       unsigned int hooknum,

                       const void *targinfo,

                       void *userinfo)

{

     structip_conntrack *ct;

     enumip_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);

 

     return ip_nat_setup_info(ct, targinfo, hooknum);

}

 

ipt_dnat_targetipt_snat_target差不多,都是调用ip_nat_setup_info完成地址转换,这里的targinfo参数来自ipt_entry_target结构的unsigned char data[0]参数,一个长度为0的数组,指向target的末尾

static unsigned intipt_dnat_target(struct sk_buff **pskb,

                       const struct net_device *in,

                       const struct net_device *out,

                       unsigned int hooknum,

                       const void *targinfo,

                       void *userinfo)

{

     structip_conntrack *ct;

     enumip_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));

 

     return ip_nat_setup_info(ct, targinfo,hooknum);

}

 

2.4          ip_nat_setup_info()函数  ip_nat_rule.c

 

unsigned int

ip_nat_setup_info(structip_conntrack *conntrack,   /* 数据包的连接状态 */

           const struct ip_nat_multi_range *mr,       /* 转换后的地址池 */

           unsigned int hooknum)                    /* hook */

{

     structip_conntrack_tuple new_tuple, inv_tuple, reply;

     structip_conntrack_tuple orig_tp;

     structip_nat_info *info = &conntrack->nat.info;

     intin_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_IN

              || hooknum == NF_IP_LOCAL_OUT);

     IP_NF_ASSERT(info->num_manips< IP_NAT_MAX_MANIPS);

     IP_NF_ASSERT(!(info->initialized& (1 << HOOK2MANIP(hooknum))));

 

     /* 对当前状态的应答方向的tuple调用invert_tuplepr取反,得到一个orig_tp,如果之前没有进行过地址或端口转换,通常这里得到的orig_tp就等于初始方向的tuple */

     invert_tuplepr(&orig_tp,conntrack->tuplehash[IP_CT_DIR_REPLY].tuple);

 

     do{

     /* 进行地址转换,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);

              returnNF_DROP;

         }

 

     /* new_tuple取反,得到经过转换后的应答方向的tuple */

         invert_tuplepr(&reply,&new_tuple);

 

     /* 修改conntrack中的应答方向的reply tuple,在这之前还要检查如果该reply tuple已经在hash表里存在了,即被其它连接占用(存在初始方向tuple不同,应答方向tuple相同的连接),则还要回头继续修改 */

     }while (!ip_conntrack_alter_reply(conntrack,&reply));

 

     /* orig_tp取反,实际上又得到了原conntrackreply_tuple…… */

     invert_tuplepr(&inv_tuple,&orig_tp);

 

     /* 将所作转换的相关信息保存到连接状态conntrack里,这样该连接的后续数据包就可以直接利用这些信息进行地址转换,不用重新查找nat表了 */

     /* 如果是源地址改变(SNAT */

     if(!ip_ct_tuple_src_equal(&new_tuple, &orig_tp)) {

         /*In this direction, a source manip. */

         info->manips[info->num_manips++]=

              ((structip_nat_info_manip)

               { IP_CT_DIR_ORIGINAL, hooknum,

                 IP_NAT_MANIP_SRC, new_tuple.src });

 

         IP_NF_ASSERT(info->num_manips< IP_NAT_MAX_MANIPS);

 

         /* 在相对的hook点上必然有对应的目的地址改变(DNAT */

         info->manips[info->num_manips++]=

              ((structip_nat_info_manip)

                   /* opposite_hook即是求当前hook点的对应hook */

               { IP_CT_DIR_REPLY, opposite_hook[hooknum],

                 IP_NAT_MANIP_DST, orig_tp.src });

         IP_NF_ASSERT(info->num_manips<= IP_NAT_MAX_MANIPS);

     }

 

     /* 如果是目的地址改变(DNAT */

     if(!ip_ct_tuple_dst_equal(&new_tuple, &orig_tp)) {

         /*In this direction, a destination manip */

         info->manips[info->num_manips++]=

              ((structip_nat_info_manip)

               { IP_CT_DIR_ORIGINAL, hooknum,

                 IP_NAT_MANIP_DST, reply.src });

 

         IP_NF_ASSERT(info->num_manips< IP_NAT_MAX_MANIPS);

 

         /*In the reverse direction, a source manip. */

         info->manips[info->num_manips++]=

              ((structip_nat_info_manip)

               { IP_CT_DIR_REPLY, opposite_hook[hooknum],

                 IP_NAT_MANIP_SRC, inv_tuple.src });

         IP_NF_ASSERT(info->num_manips<= IP_NAT_MAX_MANIPS);

     }

 

     /* 如果这个连接不是某个连接的预期的连接(子连接),则在全局链表helpers查找对应的ip_nat_helper结构 */

     if(!conntrack->master)

         info->helper= LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, &reply);

 

     /* 转换完了,标记一下 */

     info->initialized|= (1 << HOOK2MANIP(hooknum));

 

     /* 将所做的地址转换的数据结构加入到全局hashbysourcebyipsproto中,如果该地址转换是某地址转换基础上的再次转换,则用replace_in_hashes替换,反之则用place_in_hashes */

     if(in_hashes) {

         IP_NF_ASSERT(info->bysource.conntrack);

         replace_in_hashes(conntrack,info);

     }else {

         place_in_hashes(conntrack,info);

     }

 

     returnNF_ACCEPT;

}

 

 

2.5          get_unique_tuple ()函数  ip_nat_core.c

get_unique_tuple,获得一个唯一的tuple,就是说除了要做地址/段口的转换,还要保证转换得到的tuple是唯一的。

很复杂的一个函数。。。

第三个参数是用来替换的地址或端口的范围

static int

get_unique_tuple(structip_conntrack_tuple *tuple,

          const struct ip_conntrack_tuple *orig_tuple,

          const struct ip_nat_multi_range *mrr,

          struct ip_conntrack *conntrack,

          unsigned int hooknum)

{

     structip_nat_protocol *proto

         =find_nat_proto(orig_tuple->dst.protonum);

     structip_nat_range *rptr;

     unsignedint i;

     intret;

 

     structip_nat_multi_range *mr = (void *)mrr;

 

     /* 下面这一段比较晕,和p2p,udp打洞等技术有关。 */

     if(hooknum == NF_IP_POST_ROUTING) {

     /* ip_conntrack_manip结构包含一个ip地址和一个协议端口 */

         structip_conntrack_manip *manip;

     /* find_appropriate_src函数先调用hash_by_src函数计算orig_tuplehash值,然后去bysource表里查找,如果能找到源地址和端口都匹配的连接,并且如果该连接的地址/端口本身就满足目标地址/端口范围的话,就直接返回查到的这个连接的源ip */

         manip= find_appropriate_src(orig_tuple, mr);

         if(manip) {

              /* Apply samesource manipulation. */

              *tuple= ((struct ip_conntrack_tuple)

                     { *manip, orig_tuple->dst });

              DEBUGP("get_unique_tuple:Found current src map\n");

              /* 还要保证连接跟踪表里没有这个连接 */

              if(!ip_nat_used_tuple(tuple, conntrack))

                   return1;

         }

     }

 

     /* orig_tuple是转换之前的,tuple是转换之后的 */

     *tuple= *orig_tuple;

     /* 循环     ,尝试mr参数所指定的地址/端口范围,直到能满足其tuple是唯一的 */

     while((rptr = find_best_ips_proto_fast(tuple, mr,conntrack, hooknum))

            != NULL) {

         DEBUGP("Foundbest for "); DUMP_TUPLE(tuple);

     /*  IP_NAT_MANIP_SRC, 进行SNAT

IP_NAT_MANIP_DST 进行DNAT

IP_NAT_RANGE_MAP_IPS range里指定了IP地址

IP_NAT_RANGE_PROTO_SPECIFIED range里指定了port

如果没有指定协议端口范围,或者满足了所指定的范围 */

         if((!(rptr->flags & IP_NAT_RANGE_PROTO_SPECIFIED)

              || proto->in_range(tuple, HOOK2MANIP(hooknum),

                       &rptr->min,&rptr->max))

             && !ip_nat_used_tuple(tuple,conntrack)) {

              ret= 1;

              gotoclear_fulls;

         }else {

              if(proto->unique_tuple(tuple, rptr,

                            HOOK2MANIP(hooknum),

                            conntrack)){

                   /*Must be unique. */

                   IP_NF_ASSERT(!ip_nat_used_tuple(tuple,

                                     conntrack));

                   ret= 1;

                   gotoclear_fulls;

              }else if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {

                   /*Try implicit source NAT; protocol

                                   may be ableto play with ports to

                                   make itunique. */

                   structip_nat_range r

                       ={ IP_NAT_RANGE_MAP_IPS,

                           tuple->src.ip, tuple->src.ip,

                           { 0 }, { 0 } };

                   DEBUGP("Tryingimplicit mapping\n");

                   if(proto->unique_tuple(tuple, &r,

                                 IP_NAT_MANIP_SRC,

                                 conntrack)){

                       /*Must be unique. */

                       IP_NF_ASSERT(!ip_nat_used_tuple

                                 (tuple, conntrack));

                       ret= 1;

                       gotoclear_fulls;

                   }

              }

              DEBUGP("Protocolcan't get unique tuple %u.\n",

                     hooknum);

         }

 

         /*Eliminate that from range, and try again. */

         rptr->flags|= IP_NAT_RANGE_FULL;

         *tuple= *orig_tuple;

     }

 

     ret= 0;

 

 clear_fulls:

     /*Clear full flags. */

     IP_NF_ASSERT(mr->rangesize>= 1);

     for(i = 0; i < mr->rangesize; i++)

         mr->range[i].flags&= ~IP_NAT_RANGE_FULL;

 

     returnret;

}

 

 

你可能感兴趣的:(netfilter之NAT代码解读)