我无法正确修改内核模块中的ICMP流量

时间:2012-04-09 07:31:35

标签: linux linux-kernel kernel-module

所以这就是我想要做的事情(琐碎,我知道;我这样做是为了学习一个项目的东西): 我已经构建了这个模块来捕获所有传出流量,检查它是否是ICMP回送消息流量。 如果是,它只是重新计算ICMP数据包的校验和,然后让它继续运行。

每次我修改此模块时,所有PING流量都会失败>。 你能告诉我这里我做错了什么吗?

/* 
        Coder: Adel *. *******
    Creation Date: April/7th/2012
    Last Modification Date: April/9th/2012
    Purpose: This module is merely a prototype on how to change the IP/ICMP pakcet information and still let it go without problems
    Testing: This module is being tested on a machine running the Linux kernel 2.6.32-33 on a 64bits Intel Processor    
    Notes: N/A
 */


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/inet.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/tcp.h>
#include <linux/in.h>

#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>

static struct nf_hook_ops nfho;
static void printICMPHeader(struct icmphdr *icmph);

/*
 * in_cksum --
 * Checksum routine for Internet Protocol
 * family headers (C Version)
 */
unsigned short in_cksum(unsigned short *addr, int len)
{
    register int sum = 0;
        u_short answer = 0;
        register u_short *w = addr;
        register int nleft = len;
        /*
        * Our algorithm is simple, using a 32 bit accumulator (sum), we add
        * sequential 16 bit words to it, and at the end, fold back all the
        * carry bits from the top 16 bits into the lower 16 bits.
        */
        while (nleft > 1)
        {
          sum += *w++;
          nleft -= 2;
        }
        /* mop up an odd byte, if necessary */
        if (nleft == 1)
        {
          *(u_char *) (&answer) = *(u_char *) w;
          sum += answer;
        }
        /* add back carry outs from top 16 bits to low 16 bits */
        sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
        sum += (sum >> 16);             /* add carry */
        answer = ~sum;              /* truncate to 16 bits */
        return (answer);
}

static unsigned int icmp_check(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 icmphdr *icmph;
    struct tcphdr *tcph;

    if(skb == NULL)
        return -1;
    iph = ip_hdr(skb);
    if(iph->protocol == IPPROTO_ICMP){
        printk(KERN_DEBUG"ICMP traffic!\n");
        icmph = icmp_hdr(skb);
        if(icmph->type == ICMP_ECHO){
            printICMPHeader(icmph);
            icmph->checksum = in_cksum((unsigned short *)icmph, sizeof(struct icmphdr));
            printICMPHeader(icmph);
        }
    }/* If IPPROTO_ICMP */
    return NF_ACCEPT;
}


static void printICMPHeader(struct icmphdr *icmph)
{
    printk(KERN_DEBUG "ICMP print function begin \n");
    printk(KERN_DEBUG "ICMP type = %d\n", icmph->type);
    printk(KERN_DEBUG "ICMP code = %d\n", icmph->code);
    printk(KERN_DEBUG "ICMP checksum = %d\n", icmph->checksum);
    printk(KERN_DEBUG "ICMP id = %d\n", icmph->un.echo.id);
    printk(KERN_DEBUG "ICMP sequence = %d\n", icmph->un.echo.sequence);
    printk(KERN_DEBUG "ICMP print function exit \n");       
}


static int __init startup(void)
{
        printk(KERN_INFO "Loading Test module...\n");
        printk(KERN_ALERT "Hello world\n");

        /* Fill in our hook structure */
        nfho.hook = icmp_check;         /* Handler function */
        nfho.hooknum  = NF_INET_POST_ROUTING; /* Just before it hits the wire */
        nfho.pf       = PF_INET;
        nfho.priority = NF_IP_PRI_FILTER;   
        nf_register_hook(&nfho);
    //pinger();
    return 0;
}

static void __exit cleanup(void)
{
    nf_unregister_hook(&nfho);  
    printk(KERN_ALERT "Goodbye Mr.\n");
}

module_init(startup);
module_exit(cleanup);

修改 为了稍微调试代码,我已经创建了自己的用户空间ping实用程序,并使用RAW_SOCKETS填充了所有IP和ICMP标头

 icmp->type         = ICMP_ECHO;
    icmp->code          = 0;
    icmp->un.echo.id        = 0;
    icmp->un.echo.sequence  = 0;
    icmp-> checksum     = in_cksum((unsigned short *)icmp, sizeof(struct icmphdr));

只要我的模块未加载,此实用程序就可以正常工作。 奇怪的是,当我加载我的模块并检查内核调试文件时,看看我得到了什么:

Apr  9 10:42:10 DHS-1022CYB kernel: [ 2521.862356] ICMP traffic!
Apr  9 10:42:58 DHS-1022CYB kernel: [ 2569.572346] ICMP traffic!
Apr  9 10:43:22 DHS-1022CYB kernel: [ 2593.317201] ICMP traffic!
Apr  9 10:43:56 DHS-1022CYB kernel: [ 2627.331320] ICMP traffic!
Apr  9 10:44:05 DHS-1022CYB kernel: [ 2636.802236] ICMP traffic!
Apr  9 10:44:08 DHS-1022CYB kernel: [ 2639.876490] ICMP traffic!
Apr  9 10:45:27 DHS-1022CYB kernel: [ 2718.422229] ICMP traffic!

这基本上意味着我出于某些奇怪的原因,甚至无法捕捉到我模块中的ECHO流量! (当我无法抓住它时,它只是熄灭并完美地工作) P.S我试图将钩子改为LOCAL_OUT并得到相同的结果

EDIT2:更改了DEBUG文件的结果

Apr  9 10:57:24 DHS-1022CYB kernel: [ 3435.916336] ICMP print function exit 
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922656] ICMP traffic!
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922665] ICMP print function begin 
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922670] ICMP type = 8
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922674] ICMP code = 0
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922677] ICMP checksum = 50252
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922681] ICMP id = 3673
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922685] ICMP sequence = 512
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922688] ICMP print function exit 
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922691] ICMP print function begin 
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922695] ICMP type = 8
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922698] ICMP code = 0
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922702] ICMP checksum = 11090
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922705] ICMP id = 3673
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922709] ICMP sequence = 512
Apr  9 10:57:25 DHS-1022CYB kernel: [ 3436.922712] ICMP print function exit 

但请注意,这是Linux实用程序ping的结果,而不是我手写的PING(我仍然因某种原因无法拦截)。 只要我的模块已加载,Linux ping就无法运行。

2 个答案:

答案 0 :(得分:1)

看起来你错误地计算了校验和,包括未初始化的校验和字段本身:

icmph->checksum = in_cksum((unsigned short *)icmph, sizeof(struct icmphdr));

AVRnet docs表示在计算校验和之前,校验和字段应初始化为0。所以,试试,简单地说:

icmph->checksum = 0;
icmph->checksum = in_cksum((unsigned short *)icmph, sizeof(struct icmphdr));

这真是一个猜测;我从来没有遇到过编写TCP / IP的不幸:D但是我认为,即使内核足够聪明,可以将其初始化为0以进行校验和编码,你也需要重新校验和,所以这个成为一个问题。

答案 1 :(得分:1)

您没有正确计算校验和...正如您可以在日志中看到的那样。 ICMP校验和是在整个消息上计算的,而不仅仅是标题。所以在你的情况下:

icmph->checksum = in_cksum((unsigned short *)icmph, sizeof(struct icmphdr));

应该是:

icmph->checksum = 0;
icmph->checksum = in_cksum((unsigned short *)icmph,  
                            ntohs(iph->tot_len) - (iph->ihl << 2));

另外,不要忘记将字段初始化为0。