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,它捕获传入的数据包,因此除非您使用一些内核函数来传输您构建的数据包,否则返回 NF_ACCEPT 只会让您更改(或没有)的数据包继续在途中(这是本地系统,而不是本地系统)。
阅读类似的功能dev_queue_xmit(struct sk_buff *),但请注意,在使用此功能之前,您的 SKB 必须具有链路层标头,因为此功能实际上将您的数据包排队在队列中以立即发送到 NIC,并且您的工作是设置链路层地址.

其次,请记住,在更改 IP 标头地址后,您必须重新计算数据包的校验和,否则您的数据包将在另一端被丢弃。

第三,请注意,在内核空间中做你想做的事情在很大程度上被认为是一种非常糟糕的做法。内核模块的存在是有原因的,这不是其中之一,netfilter 是一个很棒的工具,但它实际上不应该用于发送正常流量。

编辑:

阅读您的最新评论;我建议您阅读有关 libPCap 库的信息,它应该可以很好地为您服务,并且仍然可以将您的工作保持在正确的位置,即用户空间。

于 2012-10-26T15:34:40.823 回答
1

In addition to the previous answer, another technique that can be used to echo an UDP packet from the netfilter callback is:

send the packet back, as a new UDP packet, using:

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

in net/socket.c

or also using the netpoll library, as said in the answer here.

The original packet can than be dropped using NF_DROP.

In the netfilter callback, that runs in an "interrupt context" is possible to send packets, but is not possible to receive them (since every attempt to wait causes a kernel panic). For this reason the solution I proposed works with UDP, but cannot work with TCP (the TCP handshake requires that the ACK message must be received).

Anyway, as already said, doing this kind of things in kernel space is BAD and should be used only for learning purposes.

于 2014-11-12T11:24:26.540 回答