netfilter源码学习(4)——NAT处理(1)

作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以*拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
在前面的netfilter源码学习中,我学习了netfilter的基本框架,其这部分代码看起来还是比较简单的。后来在zhangyd6这位朋友的提示下,想起自己居然遗漏了netfilter作为防火墙的主要功能NAT。这部分代码应该还比较有意思。今天开始看这部分代码。

注:tuple即为五元组,源地址,目的地址,源端口,目的端口,和协议。

NAT规则的源代码位于net/ipv4/netfilter/nf_nat_rule.c中。在函数nf_nat_rule_init注册了两个NAT的target。
  1.     ret = xt_register_target(&ipt_snat_reg);
  2.     if (ret != 0)
  3.         goto unregister_table;

  4.     ret = xt_register_target(&ipt_dnat_reg);
  5.     if (ret != 0)
  6.         goto unregister_snat;
以ipt_snat_reg为例:
  1. static struct xt_target ipt_snat_reg __read_mostly = {
  2.     .name        = "SNAT",
  3.     .target        = ipt_snat_target, //SNAT target的执行函数
  4.     .targetsize    = sizeof(struct nf_nat_multi_range_compat),
  5.     .table        = "nat",
  6.     .hooks        = (1 NF_INET_POST_ROUTING) | (1 NF_INET_LOCAL_IN),
  7.     .checkentry    = ipt_snat_checkentry, //添加SNAT规则的检查函数
  8.     .family        = AF_INET,
  9. };
先看简单的ipt_snat_checkentry
  1. static int ipt_snat_checkentry(const struct xt_tgchk_param *par)
  2. {
  3.     /* 
  4.     这个为NAT的范围,即IP,port等。虽然该结构体的名字为multi,但是目前只支持一个range
  5.     该结构体的含义看其定义很明显,这里就说明了
  6.     */
  7.     const struct nf_nat_multi_range_compat *mr = par->targinfo;
     
     //只支持一个范围。
  1.     /* Must be a valid range */
  2.     if (mr->rangesize != 1) {
  3.         pr_info("SNAT: multiple ranges no longer supported\n");
  4.         return -EINVAL;
  5.     }
  6.     return 0;
  7. }

Ok,那么下面进入关键的函数ipt_snat_target
  1. static unsigned int
  2. ipt_snat_target(struct sk_buff *skb, const struct xt_action_param *par)
  3. {
  4.     struct nf_conn *ct;
  5.     enum ip_conntrack_info ctinfo;
  6.     const struct nf_nat_multi_range_compat *mr = par->targinfo;

     /* SNAT只能应用于POST_ROUTING和LOCAL IN */
  1.     NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING ||
  2.          par->hooknum == NF_INET_LOCAL_IN);

     /* 
     得到conn track的信息,conn track为netfilter的一个基础。留在以后学习。
     目前我们只需要知道netfilter保存了数据包的连接信息。
     */
  1.     ct = nf_ct_get(skb, &ctinfo);

  2.     /* Connection must be valid and new. */
  3.     /* 
  4.     这里对conn进行了验证。
  5.     要做SNAT,必须是新建的连接——很明显的道理。
  6.     但是有的7层应用,可能需要多条相关的conn,这时就需要IP_CT_RELATED。
  7.     */
  8.     NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
  9.              ctinfo == IP_CT_RELATED IP_CT_IS_REPLY));
  10.     NF_CT_ASSERT(par->out != NULL);

  11.     return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_SRC);
  12. }
进入nf_nat_setup_inifo
  1. unsigned int
  2. nf_nat_setup_info(struct nf_conn *ct,
  3.          const struct nf_nat_range *range,
  4.          enum nf_nat_manip_type maniptype)
  5. {
  6.     struct net *net = nf_ct_net(ct);
  7.     struct nf_conntrack_tuple curr_tuple, new_tuple;
  8.     struct nf_conn_nat *nat;
  9.     int have_to_hash = !(ct->status & IPS_NAT_DONE_MASK);

  10.     /* nat helper or nfctnetlink also setup binding */
  11.     /* 
  12.     对于netfilter的nf_conn,其使用一个struct nf_ct_ext的结构来保存各种extension信息,如NAT。
  13.     这里尝试从ct中获得nat。
  14.     注意ct中可以保存多个extension
  15.     */
  16.     nat = nfct_nat(ct);
  17.     if (!nat) {
  18.         /* 
  19.         没有NAT的extension信息,那么就申请一个extension结构并保存在ct中
  20.         */
  21.         nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
  22.         if (nat == NULL) {
  23.             pr_debug("failed to add NAT extension\n");
  24.             return NF_ACCEPT;
  25.         }
  26.     }

  27.     NF_CT_ASSERT(maniptype == IP_NAT_MANIP_SRC ||
  28.          maniptype == IP_NAT_MANIP_DST);
  29.     BUG_ON(nf_nat_initialized(ct, maniptype));

  30.     /* What we've got will look like inverse of reply. Normally
  31.      this is what is in the conntrack, except for prior
  32.      manipulations (future optimization: if num_manips == 0,
  33.      orig_tp =
  34.      conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */
  35.     /* 
  36.     这里要注意一点:通过reply方向的tuple信息,来得到当前的tuple信息,即发送方向的tuple。
  37.     那么为什么不直接使用ct的IP_CT_DIR_ORIGINAL的tuple信息呢。
  38.     根据上面的注释所说,一般情况下可以使用ORIGINAL方向的tuple信息。但是如果num_manips不为0,
  39.     那么original方向的tuple信息就不能使用。因为original的信息被修改了??
  40.     */
  41.     nf_ct_invert_tuplepr(&curr_tuple,
  42.              &ct->tuplehash[IP_CT_DIR_REPLY].tuple);

     /* 
     得到SNAT后的tuple。get_unique_tuple留在后面学习。
     */
  1.     get_unique_tuple(&new_tuple, &curr_tuple, range, ct, maniptype);

  2.     if (!nf_ct_tuple_equal(&new_tuple, &curr_tuple)) {
  3.         /* SNAT后的tuple与之前的tuple不等,即tuple发生了变化 */
  4.         struct nf_conntrack_tuple reply;

  5.         /* Alter conntrack table so will recognize replies. */
  6.         /* tuple变化了,所以需要更新conntrack中的REPLY方向的tuple */
  7.         nf_ct_invert_tuplepr(&reply, &new_tuple);
  8.         nf_conntrack_alter_reply(ct, &reply);

  9.         /* Non-atomic: we own this at the moment. */
  10.         if (maniptype == IP_NAT_MANIP_SRC)
  11.             ct->status |= IPS_SRC_NAT;
  12.         else
  13.             ct->status |= IPS_DST_NAT;
  14.     }

  15.     /* Place in source hash if this is the first time. */
  16.     /* 
  17.     如注释所说,第一次做NAT时将,ORIGINAL方向的tuple加入到hash表中。
  18.     虽然这里只是将tuple加入到hash表中,但是实际上我们可以从tuple得到conntrack结构。
  19.     不知道以后会不会有这样的操作?
  20.     */
  21.     if (have_to_hash) {
  22.         unsigned int srchash;

  23.         srchash = hash_by_src(net, nf_ct_zone(ct),
  24.                  &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
  25.         spin_lock_bh(&nf_nat_lock);
  26.         /* nf_conntrack_alter_reply might re-allocate exntension aera */
  27.         nat = nfct_nat(ct);
  28.         nat->ct = ct;
  29.         hlist_add_head_rcu(&nat->bysource,
  30.                  &net->ipv4.nat_bysource[srchash]);
  31.         spin_unlock_bh(&nf_nat_lock);
  32.     }

  33.     /* It's done. */
  34.     if (maniptype == IP_NAT_MANIP_DST)
  35.         set_bit(IPS_DST_NAT_DONE_BIT, &ct->status);
  36.     else
  37.         set_bit(IPS_SRC_NAT_DONE_BIT, &ct->status);

  38.     return NF_ACCEPT;
  39. }
明天再看get_unique_tuple,另外,似乎不把conn track搞定,看这部分代码会有困惑。我来看看conn track,决定先研究一下哪个

上一篇:《 产品设计思维:电商产品设计全攻略》一一第3章 电商梦的开始:首页设计


下一篇:06 pandas DataFrame - 数据过滤、NaN处理、统计方法