smillers 发表于 2014-06-02 10:05

请教一个问题,Netfilter过滤URL,遇到视频类网站内核崩溃

如题,用Netfilter写了一段代码来打印HOST和URI信息,编译后可正常打印出URL,当浏览器打开腾讯视频等一些内容的时候,内核就崩溃了,麻烦各位大牛帮忙分析一下,谢谢!
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif

#include   <linux/module.h>
#include   <linux/kernel.h>
#include   <linux/skbuff.h>
#include   <linux/in.h>
#include   <linux/ip.h>
#include   <linux/tcp.h>
#include   <linux/icmp.h>
#include   <linux/netdevice.h>
#include   <linux/netfilter.h>
#include   <linux/netfilter_ipv4.h>
#include   <linux/if_arp.h>
#include   <linux/if_ether.h>
#include   <linux/if_packet.h>
#include   <linux/mm.h>
#include   <linux/fs.h>
#include   <linux/uaccess.h>
#include   <asm/uaccess.h>

//#define    DEBUG

MODULE_LICENSE("GPL");

char* h_tmp ;         //HOST
char* u_tmp ;         //URI


static struct nf_hook_ops nfho;


//获取HOST
void print_host(const unsigned char* start,unsigned int len)
{
      char* host_tmp ;
      char* url_tmp ;
      int i = 0;
      int j = 0;
      if (len > 4)
      {
         if (0 == strncmp(start, "GET", 3) || 0 == strncmp(start, "POST", 4))
         {
                /*获取HOST的值*/
                   host_tmp = strstr(start,"Host:");
                   if(memcmp(host_tmp,"Host:",5) == 0)
                   {
                           host_tmp = host_tmp + 6;               // 跳过HOST: 这6个字符
                           while(*host_tmp!='\r')
                           {
                                 host_tmp++;
                                 i++;
                           }
                              host_tmp -= i;
                              h_tmp = kmalloc((i + 1)*sizeof(char),GFP_KERNEL);
                              memcpy(h_tmp,host_tmp,i);
                              *(h_tmp+i) ='\0';
                              printk(">>>Host:%s",h_tmp);
                              kfree(h_tmp);
                   }
                /*获取GET后面的参数*/
                           url_tmp = strstr(start,"/");            /*从/开始获取GET后面的URI部分*/
                           while(*url_tmp!=' ')                              /*到空格的地方结束*/
                              {
                                 url_tmp++;
                                 j++;
                              }
                           url_tmp -=j;
                           u_tmp = kmalloc((j + 1)*sizeof(char),GFP_KERNEL);
                           memcpy(u_tmp,url_tmp,j);
                           *(u_tmp+j) ='\0';
                           printk("%s\n",u_tmp);
                           kfree(u_tmp);
                   }
         }
}

/* HOOK函数 */
unsigned int hook_func(unsigned int hooknum,
                                           struct sk_buff *skb,
                                           const struct net_device *in,
                                           const struct net_device *out,
                                           int (*okfn)(struct sk_buff *))
{
      char * payload = NULL;
      struct sk_buff* sb = skb;                                                                                        //定义一个Linux网络数据包缓存
      struct iphdr *iph = ip_hdr(sb);                                                                        //定义一个ip报文的数据报头

/*读取文件结束,过滤掉skb_buf为空以及iph为空的数据包*/
      if(sb == NULL)
          return NF_ACCEPT;
      if(iph == NULL)
          return NF_ACCEPT;

/*判断是否为TCP协议,不是TCP协议的数据包直接return NF_ACCEPT*/
      if (iph && iph->protocol && (iph->protocol == IPPROTO_TCP)) {
                //struct tcphdr* tcph = (void *)iph + iph->ihl*4;                           //TCP报文的头部
                struct tcphdr* tcph =(void *)iph +(iph->ihl<<2);
                unsigned int srcport = tcph->source;                                                 //源端口
                unsigned int dstport = tcph->dest;                                                   //目的端口
                unsigned int ack_seq_num = tcph->ack_seq;                                                   //ACK-SEQ的值
                unsigned int seq_num = tcph->seq;                                                            //SEQ的值
                unsigned int ip_saddr = ntohl(iph->saddr);                                                //源IP
                unsigned int ip_daddr = ntohl(iph->daddr);                                                //目的IP
                unsigned int len_tot = ntohs(iph->tot_len);                                                //包总长度
                unsigned int len_tcp = len_tot - iph->ihl*4 - tcph->doff*4;         //TCP包的长度

/*判断tcph是否为空,为空直接返回NF_ACCEPT*******/
            if(tcph == NULL)
               return NF_ACCEPT;
/*判断目的端口是否为80************************/
                if (ntohs(dstport) == 80 )                                                                                 //筛选远端口或者目的
                {
                        if (0 != skb_linearize(sb))
                           return NF_ACCEPT;

//                   payload = (char*)iph+(iph->ihl*4) + tcph->doff*4;
                   payload = (void *)tcph + (tcph->doff<<2);
                   print_host(payload,len_tcp);                                                                                     //DEBUG模式下打印HOST 和 URL

                  if (0 == strncmp(payload, "GET", 3) || 0 == strncmp(payload, "POST", 4))
                         {

                                       printk("**************Request**************\n");
                                       printk("In-Device :%s         Out-Device:%s\n",in->name,out->name);
                                       printk("Src IP    :%d.%d.%d.%d \n", (ip_saddr & 0xff000000) >> 24,
                                                                                                                  (ip_saddr & 0x00ff0000) >> 16,
                                                                                                                  (ip_saddr & 0x0000ff00) >> 8,
                                                                                                                  (ip_saddr & 0x000000ff));
                                       printk("Dst IP    :%d.%d.%d.%d \n", (ip_daddr & 0xff000000) >> 24,
                                                                                                               (ip_daddr & 0x00ff0000) >> 16,
                                                                                                                (ip_daddr & 0x0000ff00) >> 8,
                                                                                                               (ip_daddr & 0x000000ff));
                                       printk("ID      :%u\n",ntohs(iph->id));
                                       printk("Src-Port:%d         Dst-Port:%d\n", ntohs(srcport), ntohs(dstport));
                                       printk("Seq       :0x%2x\n", ntohl(seq_num));
                                       printk("Ack_Seq   :0x%2x\n", ntohl(ack_seq_num));
                                       printk("Tcp len   :%u\n",len_tcp);
                                       printk("Next ack:%u\n",ntohl(seq_num + len_tcp ));
                                       printk("*******************************\n");
                                       return NF_ACCEPT;
                         }
                         return NF_ACCEPT;
                }
      }

      return NF_ACCEPT;
}

/* 加载模块 */
int init_module() {
      nfho.hook = hook_func;                                     //钩子函数指针
      nfho.hooknum = NF_INET_POST_ROUTING;    //hook类型
      nfho.pf = PF_INET;                                          //协议簇,对于ipv4而言,是PF_INET
      nfho.priority = NF_IP_PRI_FIRST;            //优先级
      nf_register_hook(&nfho);

      pr_info("Filter add into kernel!\r\n");
      return 0;
}
/* 清除模块 */
void cleanup_module() {
      nf_unregister_hook(&nfho);
      pr_info("Filter removed from kernel!\r\n");
}

Godbach 发表于 2014-06-02 22:52

回复 1# smillers
其实如果内核 oops 了,应该有一些有用的新的。

简单看了一下你解析 TCP 报文的问题,首先一个问题没有考虑,就是分片包的问题。如果是 TCP 的分片报文,那么根本就没有所谓的 tcp header 让你解引用了。

此外,一个吹毛求疵的小问题:
      if(iph == NULL)
          return NF_ACCEPT;

/*判断是否为TCP协议,不是TCP协议的数据包直接return NF_ACCEPT*/
      if (iph && iph->protocol && (iph->protocol == IPPROTO_TCP)) {
最后那个条件判断,可以简化了为

if (iph->protocol == IPPROTO_TCP)

你 iph 都判断过了,没必要多两个冗余的条件。

   

humjb_1983 发表于 2014-06-03 08:38

具体可能还得通过kdump,或者串口抓点信息来看。
也可能是printk打印太频繁导致?

kkddkkdd11 发表于 2014-06-03 10:42

Godbach 发表于 2014-06-02 22:52 static/image/common/back.gif
回复 1# smillers
其实如果内核 oops 了,应该有一些有用的新的。



版主说的这个,可以线性化一下

huangbanban 发表于 2014-06-05 19:12

本帖最后由 huangbanban 于 2014-06-05 19:17 编辑

host_tmp = strstr(start,"Host:");
url_tmp = strstr(start,"/");
strstr函数的使用让我有些不安,没有限制查找的范围,因为是网络数据包
要是能够实现这样一个函数再使用,也许更安心一些吧
strnstr(char* s1, char*s2, int pos)

smillers 发表于 2014-06-09 10:18

本帖最后由 smillers 于 2014-06-09 10:23 编辑

好像是strstr()函数的问题,重写了一个函数,解决了,现在已经不崩溃了回复 5# huangbanban


   

smillers 发表于 2014-06-09 10:23

好像是strstr()函数的问题你,重写了一个,解决了,现在已经不崩溃了回复 5# huangbanban


   

huangbanban 发表于 2014-06-09 10:41

恭喜恭喜,呵呵:D)

ysprogram 发表于 2015-03-25 09:25

大哥,strstr是什么问题呀?
页: [1]
查看完整版本: 请教一个问题,Netfilter过滤URL,遇到视频类网站内核崩溃