Netfilter学习之NAT类型动态配置(七)全锥型NAT内核空间实现

 本文主要实现全锥型NAT的内核空间iptables命令行扩展对应的钩子函数及其功能的实现。实现思路见上文。

1.关键部分实现代码

(1)建立ipt_FULLCONE.c以激活钩子函数,关键在于保持和用户空间libipt的一致性。

static struct xt_target fullcone_tg_reg __read_mostly = {
    .name       = "FULLCONE",
    .family     = NFPROTO_IPV4,
    .target     = fullcone_tg,
    .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat),
    .table      = "nat",
    .hooks      = 1 << NF_INET_PRE_ROUTING,
    .checkentry = fullcone_tg_check,
    .destroy    = fullcone_tg_destroy,
    .me     = THIS_MODULE,
};

在fullcone_tg中调用nf_nat_fullcone_ipv4

static unsigned int
fullcone_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
    struct nf_nat_range range;
    const struct nf_nat_ipv4_multi_range_compat *mr;

    mr = par->targinfo;
    range.flags = mr->range[0].flags;
    range.min_proto = mr->range[0].min;
    range.max_proto = mr->range[0].max;

    return nf_nat_fullcone_ipv4(skb, xt_hooknum(par), &range,
                      xt_out(par));
}

(2)在nf_nat_fullcone_ipv4.c中实现具体功能

unsigned int
nf_nat_fullcone_ipv4(struct sk_buff *skb, unsigned int hooknum,
            const struct nf_nat_range *range,
            const struct net_device *out)
{
    struct nf_conn *ct;
    struct nf_conn_nat *nat;
    enum ip_conntrack_info ctinfo;
    struct nf_nat_range newrange;
    __be32 newdst;

    //we need to monitor packets at prerouting and put dst to nf_nat_setup_info
    NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING);

    ct = nf_ct_get(skb, &ctinfo);       //get infomationn from sockets
    nat = nfct_nat(ct);

    NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || 
               ctinfo == IP_CT_RELATED_REPLY));

    /* Source address is 0.0.0.0 - locally generated packet that is
     * probably not supposed to be masqueraded.
     */
    if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0)
        return NF_ACCEPT;

    newdst = nf_nat_fullcone_match(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);

    if (newdst)
    {
        //transfer from original range
        memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
        memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
        newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
        newrange.min_addr.ip = newdst;
        newrange.max_addr.ip = newdst;
        newrange.min_proto = range->min_proto;
        newrange.max_proto = range->max_proto;

        //Hnad modified range to generic setup. Change dst by normal way.
        return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
    }
    else
        return NF_ACCEPT;           
}
EXPORT_SYMBOL_GPL(nf_nat_fullcone_ipv4);

(3)进入nf_nat_core.c中,调用nf_nat_setup_info,详细源码介绍可见前文《Netfilter学习之NAT类型动态配置(四)nf_nat_core.c源码解析》

改动代码如下:

unsigned int
nf_nat_setup_info(struct nf_conn *ct,
          const struct nf_nat_range *range,
          enum nf_nat_manip_type maniptype)
{
    struct net *net = nf_ct_net(ct);
    struct nf_conntrack_tuple curr_tuple, new_tuple;

    /* Can't setup nat info for confirmed ct. */
    if (nf_ct_is_confirmed(ct))
        return NF_ACCEPT;

    WARN_ON(maniptype != NF_NAT_MANIP_SRC &&
        maniptype != NF_NAT_MANIP_DST);

    if (WARN_ON(nf_nat_initialized(ct, maniptype)))
        return NF_DROP;

    /* What we've got will look like inverse of reply. Normally
     * this is what is in the conntrack, except for prior
     * manipulations (future optimization: if num_manips == 0,
     * orig_tp = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple)
     */
    nf_ct_invert_tuplepr(&curr_tuple,
                 &ct->tuplehash[IP_CT_DIR_REPLY].tuple);

    get_unique_tuple(&new_tuple, &curr_tuple, range, ct, maniptype);

    if (!nf_ct_tuple_equal(&new_tuple, &curr_tuple)) {
        struct nf_conntrack_tuple reply;

        /* Alter conntrack table so will recognize replies. */
        nf_ct_invert_tuplepr(&reply, &new_tuple);
        nf_conntrack_alter_reply(ct, &reply);

        /* Non-atomic: we own this at the moment. */
        if (maniptype == NF_NAT_MANIP_SRC)
            ct->status |= IPS_SRC_NAT;
        else
            ct->status |= IPS_DST_NAT;

        if (nfct_help(ct) && !nfct_seqadj(ct))
            if (!nfct_seqadj_ext_add(ct))
                return NF_DROP;
    }

    if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip != ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip){
        listnode = (struct MatchTupleList *)kmalloc(sizeof(struct MatchTupleList), GFP_KERNEL);
        listnode->tuple.src = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src;
        listnode->tuple.dst = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst;
        listnode->specifiedIP = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip;
        list_add_tail(&listnode->list, &TupleHead.list);
    }


    if (maniptype == NF_NAT_MANIP_SRC) {
        unsigned int srchash;
        spinlock_t *lock;

        srchash = hash_by_src(net,
                      &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
        lock = &nf_nat_locks[srchash % CONNTRACK_LOCKS];
        spin_lock_bh(lock);
        hlist_add_head_rcu(&ct->nat_bysource,
                   &nf_nat_bysource[srchash]);
        spin_unlock_bh(lock);
    }

    /* It's done. */
    if (maniptype == NF_NAT_MANIP_DST)
        ct->status |= IPS_DST_NAT_DONE;
    else
        ct->status |= IPS_SRC_NAT_DONE;

    return NF_ACCEPT;
}
EXPORT_SYMBOL(nf_nat_setup_info);

2.小结

在本文中,我实现了全锥型NAT内核空间的关键部分代码,详细代码可以参考我的github上,在此基础上可以很容易的实现限制型锥形和可变的对称型等类型。

猜你喜欢

转载自blog.csdn.net/u013354486/article/details/79838240
今日推荐