Linux Netfilter nat表注册

Linux Netfilter的nat表的主要功能是修改IP地址和端口号。
nat表在4个链的位置注册:PREROUTING、POSTROUTING、LOCAL_OUT、LOCAL_IN。
注册函数如下:

static int __init nf_nat_standalone_init(void)
{
	int ret = 0;

	need_ipv4_conntrack();

#ifdef CONFIG_XFRM
	BUG_ON(ip_nat_decode_session != NULL);
	rcu_assign_pointer(ip_nat_decode_session, nat_decode_session);
#endif

    /* 注册NAT表,注册SNAT/DNAT的ipt_target */
	ret = nf_nat_rule_init();
	if (ret < 0) {
		printk("nf_nat_init: can't setup rules.\n");
		goto cleanup_decode_session;
	}

    /* 注册NAT的4个hook点 */
	ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
	if (ret < 0) {
		printk("nf_nat_init: can't register hooks.\n");
		goto cleanup_rule_init;
	}
	return ret;

 cleanup_rule_init:
	nf_nat_rule_cleanup();
 cleanup_decode_session:
#ifdef CONFIG_XFRM
	rcu_assign_pointer(ip_nat_decode_session, NULL);
	synchronize_net();
#endif
	return ret;
}

nat表的hook定义,注册到4个链:

static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
	/* Before packet filtering, change destination */
	{
		.hook		= nf_nat_in,
		.owner		= THIS_MODULE,
		.pf		= PF_INET,
		.hooknum	= NF_INET_PRE_ROUTING,
		.priority	= NF_IP_PRI_NAT_DST,
	},
	/* After packet filtering, change source */
	{
		.hook		= nf_nat_out,
		.owner		= THIS_MODULE,
		.pf		= PF_INET,
		.hooknum	= NF_INET_POST_ROUTING,
		.priority	= NF_IP_PRI_NAT_SRC,
	},
	/* Before packet filtering, change destination */
	{
		.hook		= nf_nat_local_fn,
		.owner		= THIS_MODULE,
		.pf		= PF_INET,
		.hooknum	= NF_INET_LOCAL_OUT,
		.priority	= NF_IP_PRI_NAT_DST,
	},
	/* After packet filtering, change source */
	{
        /* 注意,直接调用nf_nat_fn,此处最大可能是修改
         * 源端口号,为连接跟踪rely项服务 */
		.hook		= nf_nat_fn,
		.owner		= THIS_MODULE,
		.pf		= PF_INET,
		.hooknum	= NF_INET_LOCAL_IN,
		.priority	= NF_IP_PRI_NAT_SRC,
	},
};

nat模块PREROUTING点的hook函数,实现DNAT。

static unsigned int nf_nat_in(unsigned int hooknum,
        struct sk_buff *skb,
        const struct net_device *in,
        const struct net_device *out,
        int (*okfn)(struct sk_buff *))
{
    unsigned int ret;
    __be32 daddr = ip_hdr(skb)->daddr;

    /* 实现DNAT转换 */
    ret = nf_nat_fn(hooknum, skb, in, out, okfn);
    if (ret != NF_DROP && ret != NF_STOLEN &&
            daddr != ip_hdr(skb)->daddr)
    {
        /* 如果DNAT转换成功,目的IP发生改变,
         * 则需要对dst_entry引用计数减1,并置空skb->dst,
         * 数据包向下走会自动重新查找路由 */
        dst_release(skb->dst);
        skb->dst = NULL;
    }
    return ret;
}

nat模块POSTROUTING点的hook函数,实现SNAT。

static unsigned int nf_nat_out(unsigned int hooknum,
        struct sk_buff *skb,
        const struct net_device *in,
        const struct net_device *out,
        int (*okfn)(struct sk_buff *))
{
#ifdef CONFIG_XFRM
    const struct nf_conn *ct;
    enum ip_conntrack_info ctinfo;
#endif
    unsigned int ret;

    /* root is playing with raw sockets. */
    if (skb->len < sizeof(struct iphdr) ||
            ip_hdrlen(skb) < sizeof(struct iphdr))
        return NF_ACCEPT;

    /* 实现SNAT转换 */
    ret = nf_nat_fn(hooknum, skb, in, out, okfn);
#ifdef CONFIG_XFRM
    if (ret != NF_DROP && ret != NF_STOLEN && (ct = nf_ct_get(skb, &ctinfo)) != NULL)
    {
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);

        if (ct->tuplehash[dir].tuple.src.u3.ip != ct->tuplehash[!dir].tuple.dst.u3.ip
                || ct->tuplehash[dir].tuple.src.u.all != ct->tuplehash[!dir].tuple.dst.u.all)
            /* 如果SNAT转换成功,IP发生改变,则需要对dst_entry处理 */
            return ip_xfrm_me_harder(skb) == 0 ? ret : NF_DROP;
    }
#endif
    return ret;
}

nat模块LOCAL_OUT点的hook函数,实现DNAT。

static unsigned int nf_nat_local_fn(unsigned int hooknum,
        struct sk_buff *skb,
        const struct net_device *in,
        const struct net_device *out,
        int (*okfn)(struct sk_buff *))
{
    const struct nf_conn *ct;
    enum ip_conntrack_info ctinfo;
    unsigned int ret;

    /* root is playing with raw sockets. */
    if (skb->len < sizeof(struct iphdr) ||
            ip_hdrlen(skb) < sizeof(struct iphdr))
        return NF_ACCEPT;

    /* 实现DNAT转换 */
    ret = nf_nat_fn(hooknum, skb, in, out, okfn);
    if (ret != NF_DROP && ret != NF_STOLEN &&
            (ct = nf_ct_get(skb, &ctinfo)) != NULL)
    {
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);

        if (ct->tuplehash[dir].tuple.dst.u3.ip != ct->tuplehash[!dir].tuple.src.u3.ip)
        {
            /* 进入OUTPUT链之前已经计算过路由,后续不会再查找路由的操作,
             * 此时如果目的IP改变,则需要重新计算路由 */
            if (ip_route_me_harder(skb, RTN_UNSPEC))
                ret = NF_DROP;
        }
#ifdef CONFIG_XFRM
        else if (ct->tuplehash[dir].tuple.dst.u.all != ct->tuplehash[!dir].tuple.src.u.all)
            if (ip_xfrm_me_harder(skb))
                ret = NF_DROP;
#endif
    }
    return ret;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值