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):
 
     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;
 
    }