本文主要实现全锥型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上,在此基础上可以很容易的实现限制型锥形和可变的对称型等类型。