6

カーネル空間でパケットをエコーし​​たい。このマシンでポート 6000 を使用してエコー サーバーを実行しています。今度は、クライアントが別のマシンで実行され、エコー サーバーにデータを送信します。ここでやりたいことは、カーネル空間からパケットをエコー バックすることです。パケットでサーバーを煩わせたくないので、カーネル空間から静かにエコーされます。私は以下のコードを提示しています:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netdevice.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/crypto.h>
#include <linux/init.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <net/ip.h>
#include <net/udp.h>
#include <net/route.h>
#include <net/checksum.h>
#include <linux/netfilter_ipv4.h>

#define IP_HDR_LEN 20
#define UDP_HDR_LEN 8
#define TOT_HDR_LEN 28

static unsigned int pkt_echo_begin(unsigned int hooknum,
                        struct sk_buff *skb,
                        const struct net_device *in,
                        const struct net_device *out,
                        int (*okfn)(struct sk_buff *));

static struct nf_hook_ops pkt_echo_ops __read_mostly = {
    .pf = NFPROTO_IPV4,
    .priority = 1,
    .hooknum = NF_INET_PRE_ROUTING,
    .hook = pkt_echo_begin,
};

static int __init pkt_echo_init(void)
{
    printk(KERN_ALERT "\npkt_echo module started ...");
    return nf_register_hook(&pkt_echo_ops);
}

static void __exit pkt_echo_exit(void)
{
    nf_unregister_hook(&pkt_echo_ops);
    printk(KERN_ALERT "pkt_echo module stopped ...");
}

static unsigned int pkt_echo_begin (unsigned int hooknum,
                        struct sk_buff *skb,
                        const struct net_device *in,
                        const struct net_device *out,
                        int (*okfn)(struct sk_buff *))
{
    struct iphdr *iph;
    struct udphdr *udph;
    unsigned char *data;

    unsigned char *temp;

    __u16 dst_port, src_port;
    int data_len;



    if (skb) {
        iph = (struct iphdr *) skb_header_pointer (skb, 0, 0, NULL);

        if (iph && iph->protocol &&(iph->protocol == IPPROTO_UDP)) {
            udph = (struct udphdr *) skb_header_pointer (skb, IP_HDR_LEN, 0, NULL);
            src_port = ntohs (udph->source);
            dst_port = ntohs (udph->dest);

            if (dst_port == 6000) {
                printk(KERN_ALERT "\nUDP packet goes in");

                data = (unsigned char *) skb_header_pointer (skb, TOT_HDR_LEN, 0, NULL);
                data_len = skb->len - TOT_HDR_LEN;

                struct sk_buff *newskb;
                struct iphdr *newiph;
                struct udphdr *newudph;
                unsigned char *newdata;
                unsigned int newdata_len;

                newskb = skb_copy(skb, GFP_ATOMIC);
                newiph = (struct iphdr *) skb_header_pointer (newskb, 0, 0, NULL);
                newudph = (struct udphdr *) skb_header_pointer (newskb, IP_HDR_LEN, 0, NULL);
                newdata = (unsigned char *) skb_header_pointer (newskb, TOT_HDR_LEN, 0, NULL);

                newiph->saddr = iph->daddr;
                newiph->daddr = iph->saddr;

                newudph->source = udph->dest;
                newudph->dest = udph->source;

                struct sk_buff *tempskb;

                tempskb = skb_copy(skb, GFP_ATOMIC);

                *tempskb = *skb;
                *skb = *newskb;
                *newskb = *tempskb;

                kfree_skb(newskb);

            }
        }
    }
    return NF_ACCEPT;
}


module_init(pkt_echo_init);
module_exit(pkt_echo_exit);

MODULE_AUTHOR("Rifat Rahman Ovi: <rifatrahmanovi@gmail.com>");
MODULE_DESCRIPTION("Echoing a packet from kernel space.");
MODULE_LICENSE("GPL");

今、私は何が必要なのか理解していません。PRE_ROUTING フックでパケットをキャプチャしました。次に、新しい skb を作成し、古い受信 skb から入力し、送信元アドレス (saddr)、宛先アドレス (daddr)、送信元ポート (source)、および宛先ポート (dest) を変更して、パケットをPRE_ROUTING フックから転送されます。パケットがフックを通過した後にルーティングの決定が行われるため、パケットが発信元に転送されるのを探します。しかし、何らかの理由で、そのように行っていません。パケットが受信され、すべてが変更されますが、パケットは後方に移動していないようです。何が欠けているのか、それを機能させるために何が必要なのかわかりません。より具体的には、PRE_ROUTING フックからネットワークにパケットを送信するには何が必要ですか?

4

2 に答える 2

5

多くが欠けています。

まず、使用した netfilter フックは PRE-ROUTING であり、INCOMING パケットをキャプチャするため、カーネル関数を使用して構築したパケットを送信しない限り、NF_ACCEPT を返すと、変更した (またはしなかった) パケットのみが続行されます。途中です(ローカルシステムからではなく、ローカルシステムへ)。
次のような関数について読んdev_queue_xmit(struct sk_buff *)でください。ただし、この関数を使用する前に、SKB にリンク層ヘッダーが必要であることに注意してください。これは、この関数が実際にパケットをキューに入れ、すぐに NIC に送信するためです。リンク層アドレスを設定するのはあなたの仕事です。 .

次に、IP ヘッダー アドレスを変更した後は、パケットのチェックサムを再計算する必要があります。そうしないと、パケットが相手側で破棄されます。

第三に、カーネル空間でやろうとしていることを行うことは、非常に悪い習慣であると考えられていることに注意してください。カーネル モジュールが存在するのには理由があり、これはその 1 つではありません。netfilter は優れたツールですが、通常のトラフィックの送信に使用することは想定されていません。

編集:

最新のコメントを読む; libPCap ライブラリについて読むことをお勧めします。これは非常に役立ち、作業を適切な場所であるユーザー空間に保持する必要があります。

于 2012-10-26T15:34:40.823 に答える
1

前の回答に加えて、netfilter コールバックから UDP パケットをエコーするために使用できる別の手法は次のとおりです。

以下を使用して、パケットを新しい UDP パケットとして送り返します。

int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)

net/socket.c で

または、こちらの回答で述べたように、netpoll ライブラリも使用します。

元のパケットは、NF_DROP を使用してドロップできます。

「割り込みコンテキスト」で実行される netfilter コールバックでは、パケットを送信することはできますが、受信することはできません (待機しようとするとカーネル パニックが発生するため)。このため、私が提案したソリューションは UDP では機能しますが、TCP では機能しません (TCP ハンドシェイクでは ACK メッセージを受信する必要があります)。

とにかく、すでに述べたように、カーネル空間でこの種のことを行うのは悪いことであり、学習目的でのみ使用する必要があります。

于 2014-11-12T11:24:26.540 に答える