Chinaunix首页 | 论坛 | 博客
  • 博客访问: 273641
  • 博文数量: 72
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 276
  • 用 户 组: 普通用户
  • 注册时间: 2014-07-28 23:52
文章分类

全部博文(72)

文章存档

2017年(20)

2014年(52)

分类: LINUX

2014-10-16 16:56:56

1NAT表的创建

文件路径:/net/ipv4/netfilter/nf_nat_rule.c

 

#define NAT_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \

                                           (1 << NF_INET_POST_ROUTING) | \

                                           (1 << NF_INET_LOCAL_OUT))

 

static const struct xt_table nat_table = {

    .name = "nat",

    .valid_hooks = NAT_VALID_HOOKS,

    .me = THIS_MODULE,

    .af = NFPROTO_IPV4,

};

static int __net_init nf_nat_rule_net_init(struct net *net)

{

/*注册nat表到Netfilter*/

    net->ipv4.nat_table = ipt_register_table(net, &nat_table,

                                         &nat_initial_table.repl);

    if (IS_ERR(net->ipv4.nat_table))

        return PTR_ERR(net->ipv4.nat_table);

    return 0;

}

2、实现NAT的扩展TARGET

static struct xt_target ipt_snat_reg __read_mostly = {

    .name = "SNAT",

    .target = ipt_snat_target,

    .targetsize = sizeof(struct nf_nat_multi_range_compat),

    .table = "nat",

    .hooks = 1 << NF_INET_POST_ROUTING,

    .checkentry = ipt_snat_checkentry,

    .family = AF_INET,

};

 

static struct xt_target ipt_dnat_reg __read_mostly = {

    .name = "DNAT",

    .target = ipt_dnat_target,

    .targetsize = sizeof(struct nf_nat_multi_range_compat),

    .table = "nat",

    .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT),

    .checkentry = ipt_dnat_checkentry,

    .family = AF_INET,

};

 

int __init nf_nat_rule_init(void)

{

    int ret;
    /*注册扩展TARGET*/

    ret = xt_register_target(&ipt_snat_reg);

    ret = xt_register_target(&ipt_dnat_reg);

    return ret;

}

 

扩展target的参数定义

struct nf_nat_multi_range_compat

{

    unsigned int rangesize; /* Must be 1. */

 

    /* hangs off end. */

    struct nf_nat_range range[1];

};

 

struct nf_nat_range

{

    /* Set to OR of flags above. */

    unsigned int flags;

 

    /* Inclusive: network order. */
    
/*映射的最大和最小IP地址*/

    __be32 min_ip, max_ip;

 

    /* Inclusive: network order */
    /*映射的最大和最小端口号*/

    union nf_conntrack_man_proto min, max;

};

 

SNAT target的处理函数

static unsigned int ipt_snat_target(struct sk_buff *skb, const struct xt_target_param *par)

{

    struct nf_conn *ct;

    enum ip_conntrack_info ctinfo;
    /*取得用户配置的TARGET参数*/

    const struct nf_nat_multi_range_compat *mr = par->targinfo;
    /*SNAT target必须配置在POSTROUTING HOOK点上*/

    NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING);

 
    /*取得报文携带的conntrack信息及conntrack的状态*/

    ct = nf_ct_get(skb, &ctinfo);

 

    /* Connection must be valid and new. */
    /*conntrack 的状态必须是以下几种双方未建立连接的状态,否则就报错。

这是因为连接已建立后,报文就直接根据conntrack进行处理NAT即可,

不会再查nat使用SNAT target进行NAT的处理了*/

    NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||

    ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY));
    /*在进行SNAT前,报文必须先经过路由找到出接口,否则报错*/

    NF_CT_ASSERT(par->out != NULL);
    /*把用户配置的映射范围传给 nf_nat_setup_info进行进一步处理*/

    return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_SRC);

}

 

DNAT target的处理函数

static unsigned int ipt_dnat_target(struct sk_buff *skb, const struct xt_target_param *par)

{

    struct nf_conn *ct;

    enum ip_conntrack_info ctinfo;

    const struct nf_nat_multi_range_compat *mr = par->targinfo;

 
    /*DNAT target必须配置在 PREROUTINGOUTPUT两个hook点上*/

    NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING ||

                                 par->hooknum == NF_INET_LOCAL_OUT);

 
    /*取得报文携带的conntrack信息及conntrack的状态*/

    ct = nf_ct_get(skb, &ctinfo);

 

    /* Connection must be valid and new. */
    /*nat表进行DNAT target处理时的报文连接状态必须是如下几种未建立连接的

  状态*/

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

    /*把用户配置的映射范围传给 nf_nat_setup_info进行进一步处理*/

    return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_DST);

}

NAT target处理的核心函数

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;

    struct nf_conn_nat *nat;

    int have_to_hash = !(ct->status & IPS_NAT_DONE_MASK);

 

    /* nat helper or nfctnetlink also setup binding */
    /*如果该conntrack没有预留NAT功能使用的扩展空间,就添加扩展NAT功能

  的扩展空间*/

    nat = nfct_nat(ct);

    if (!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;

        }

    }

 

    NF_CT_ASSERT(maniptype == IP_NAT_MANIP_SRC ||

                            maniptype == IP_NAT_MANIP_DST);
    /*检查conntrack是否已经被NAT target处理过,如果处理过就报错*/

    BUG_ON(nf_nat_initialized(ct, maniptype));

 
    /*根据conntrackreply方向的tuple信息,同时根据target的配置生成经过

  NAT转换后的tupel信息,并替换原来replytuple信息*/

    nf_ct_invert_tuplepr(&curr_tuple,

             &ct->tuplehash[IP_CT_DIR_REPLY].tuple);

 
    /*根据target的配置获取一个合适NAT转换之后的IP地址和端口信息*/

    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. */
        /*更新reply tuple信息*/

        nf_ct_invert_tuplepr(&reply, &new_tuple);

        nf_conntrack_alter_reply(ct, &reply);

 

        /* Non-atomic: we own this at the moment. */
        /*设置conntrackstatus NAT*/

        if (maniptype == IP_NAT_MANIP_SRC)

            ct->status |= IPS_SRC_NAT;

        else

            ct->status |= IPS_DST_NAT;

    }

 

    /* Place in source hash if this is the first time. */
    /*把该conntrack original tuple加入到bysource hash表中,bysource hash表是

查找是否已经映射过得报文使用,即端口和地址映射表*/

    if (have_to_hash) {

        unsigned int srchash;

        srchash = hash_by_src(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);

        spin_lock_bh(&nf_nat_lock);

        /* nf_conntrack_alter_reply might re-allocate exntension aera */

        nat = nfct_nat(ct);

        nat->ct = ct;

        hlist_add_head_rcu(&nat->bysource,&net->ipv4.nat_bysource[srchash]);

        spin_unlock_bh(&nf_nat_lock);

    }

 

    /* It's done. */
    /*设置NAT_DONE位,表明该conntrack中的NAT信息已经更新完毕,以后的报文只

需要使用conntrack带的NAT信息处理报文即可,不需要在使用查NAT来处理了*/

    if (maniptype == IP_NAT_MANIP_DST)

        set_bit(IPS_DST_NAT_DONE_BIT, &ct->status);

    else

        set_bit(IPS_SRC_NAT_DONE_BIT, &ct->status);

 

    return NF_ACCEPT;

}

EXPORT_SYMBOL(nf_nat_setup_info);

 

从上面我们看到,NATtarget处理函数只是更新的conntrack中的信息,并没有修改报文。报文的修改放在了NAT的hook函数里进行处理。

(未完待续)

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