歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

終於搞定Linux的NAT即時生效問題

引:超長的前言

Linux的NAT不能及時生效,因為它是基於ip_conntrack的,如果在NAT的iptables規則添加之前,此流的數據包已經綁定了一個ip_conntrack,那麼該NAT規則就不會生效,直到此ip_conntrack過期,如果一直有數據在魯莽地嘗試傳輸,那麼就會陷入僵持狀態。
說明:命名與思路

我不知道該怎麼給自己的模塊命令,我的英文狠爛,老婆很忙又不肯幫我,又不能起一個中文名字,因此我只能使用XXX這種讓人遐想的名字,我不會使用aaa,abc這種,這樣會讓人覺得我不負責任,有點玩世不恭或者太草率等所有你能想到,並且,真實地,我也因為這種草率埃過領導的批評以及同道人的嘲諷。接受了教訓之後,我就使用XXX。

第一部分:iptables模塊文件libxt_XXX.c

/*
 *      "XXX" target extension for iptables! 其中就是一個幌子,為了使用iptables而已!
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License; either
 *      version 2 of the License, or any later version, as published by the
 *      Free Software Foundation.
 */
#include <stdio.h>
#include <xtables.h>
#include "compat_user.h"

static void xxx_tg_help(void)
{
        printf("XXX takes no options\n\n");
}

static int xxx_tg_parse(int c, char **argv, int invert, unsigned int *flags,
                         const void *entry, struct xt_entry_target **target)
{
        return 0;
}

static void xxx_tg_check(unsigned int flags)
{
}

static struct xtables_target xxx_tg_reg = {
        .version       = XTABLES_VERSION,
        .name          = "XXX",
        .revision      = 1,
        .family     = NFPROTO_IPV4,
        .help          = xxx_tg_help,
        .parse         = xxx_tg_parse,
};

static __attribute__((constructor)) void xxx_tg_ldr(void)
{
        xtables_register_target(&xxx_tg_reg);
}

iptables -t mangle -A PREROUTING ... -j XXX
第二部分:內核模塊xt_XXX.c
/*
 *      xt_xxx - kernel module to drop and re-NEW CONNTRACK to
 *              fit NAT
 *
 *      Original author: Wangran <[email protected]>
 */

#include <linux/module.h>
#include <linux/netfilter/x_tables.h>
#include <net/netfilter/nf_queue.h>
#include "compat_xtables.h"


MODULE_AUTHOR("Wanagran <[email protected]>");
MODULE_DESCRIPTION("Xtables: xxx match module");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_xxx");

/*
 * queue handler捕獲數據包,然後重新注入,區別在於:
 * 1:如果本身是NOTRACK的數據包,直接注回去;
 * 2:如果本身沒有綁定任何conntrack,直接注回去;
 * 3:如果本身有conntrack,刪掉該conntrack後,注回去
 * 3.1.不是注回原來的位置,而是注回PREROUTING最開始的位置。
 * 注意:雖然TAGEGET本身已經阻止了1,2的情況,還是判斷了一下,
 *       因為雖然我知道這一點,但是resetct_queue並不清楚...
 */

static int resetct_queue(struct nf_queue_entry *entry, unsigned queue_num)
{
        struct sk_buff *skb = entry->skb;
        struct nf_conn *ct = NULL;

        enum ip_conntrack_info ctinfo;
        if (nf_ct_is_untracked(skb))
                goto reinject;
        else if (!(ct = nf_ct_get(skb, &ctinfo)))
                goto reinject;
        else {
                // 為了重新初始化conntrack,使之狀態變為可做NAT的NEW!
                struct list_head *elem = &nf_hooks[entry->pf][entry->hook];
                nf_reset(skb);
                nf_ct_kill(ct);
                entry->elem = list_entry(elem, struct nf_hook_ops, list);
        }
reinject:
        nf_reinject(entry, NF_ACCEPT);
        return 0;

}

/*
 * XXX的執行TARGET,旨在針對以下的一類數據包進行queue處理:
 * 本身是NEW狀態,且已經被confirm了,這種數據包在其conntrack
 * 過期之前,無疑已經不會再去匹配任何NAT規則了!
 */

static unsigned int
xxx_tg4(struct sk_buff **skb, const struct xt_action_param *par)
{
        struct nf_conn *ct;
        enum ip_conntrack_info ctinfo;
        ct = nf_ct_get(*skb, &ctinfo);
        if (!ct || ct == &nf_conntrack_untracked) {
                return XT_CONTINUE;
        }
        // 僅僅處理正向數據包,否則...
        if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
                return XT_CONTINUE;
        }
        if (ctinfo == IP_CT_NEW && !nf_ct_is_confirmed(ct)) {
                return XT_CONTINUE;
        }
        return NF_QUEUE;
}


static struct nf_queue_handler xxxqh = {
        .name  = "resetct",
        .outfn = resetct_queue,
};

static struct xt_target xxx_tg_reg[] __read_mostly = {
        {
                .name           = "XXX",
                .revision       = 1,
                .family         = NFPROTO_IPV4,
                .table          = "mangle",
                .hooks          = 1 << NF_INET_PRE_ROUTING,
                .target         = xxx_tg4,
                .me             = THIS_MODULE,
        },
};
static int __init xt_xxx_target_init(void)
{
        int status = 0;
        status = nf_register_queue_handler(NFPROTO_IPV4, &xxxqh);
        if (status < 0) {
                printk("XXX: register queue handler error\n");
                goto err;
        }
        status = xt_register_targets(xxx_tg_reg, ARRAY_SIZE(xxx_tg_reg));
        if (status < 0) {
                printk("XXX: register target error\n");
                goto err;
        }

err:
        return status;
}

static void __exit xt_xxx_target_exit(void)
{
        nf_unregister_queue_handlers(&xxxqh);
        return xt_unregister_targets(xxx_tg_reg, ARRAY_SIZE(xxx_tg_reg));
}

module_init(xt_xxx_target_init);
module_exit(xt_xxx_target_exit);

第三部分:關於使用

一般而言,你可以使用下面的命令:

iptables -t mangle -A PREROUTING -j XXX

這樣的話,所有進來的數據包都會執行下面的邏輯:

 


\

如果這樣,相當於架空了整個ip_conntrack的優化,這種魯莽的做法並不是我的目的,我希望它和其它的match比如mark,condition一起使用,這樣就可以把不相關的數據包過濾掉而不觸及,依舊執行往常的邏輯,這就是我為何一直堅持使用iptables的原因而不是使用其它的用戶態/內核態通信的方式。就想之前我提到的基於ip_conntrack的快速/慢速匹配方式那樣,這個NAT及時匹配也可以使用類似的邏輯:

iptables -t mangle -A PREROUTING -m condition --condition slow ... -j XXX
Copyright © Linux教程網 All Rights Reserved