免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 24621583 | 回复: 24621583
打印 上一主题 下一主题

[网络子系统] 求助:利用netfilter和libnetfilter_queue子系统进行简单上传回传测试出现崩溃问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-07-02 10:19 |只看该作者 |倒序浏览
各位大神,我想完成一个HTTP访问控制的功能。所以想利用netfilter拦截HTTP数据包并且返回NF_QUEUE,上层应用利用libnetfilter_queue子系统进行接收处理并返回接受或丢弃。
目前测试的功能如下:
1)netfilter内核模块挂载到POST_ROUTING,检测到目的端口是80或8080后 return NF_QUEUE,否则 return NF_ACCEPT;
2) libnetfilter_queue上层应用接收到拷贝上来的数据包后,对包进行一个计数,并cout<<" This is a HTTP PACKET"<<endl;而后return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);

现在的情况是,程序运行起来貌似没问题,打开百度首页都能正常计数输出。但是我打开像新浪这种大的门户网站后因为包的数量特别多,我刷新几次之后电脑就会打印oops之后崩溃。这个问题困扰了好久,还望各位请教。
以下是源码:

(1)netfilter内核模块

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/string.h>
#include <linux/kmod.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <linux/if_arp.h>

//定义钩子函数结构体
struct nf_hook_ops  post_hook;   

//实例化钩子函数
static unsigned int watch_out(unsigned int hooknum,
                  struct sk_buff *skb,
                  const struct net_device *in,
                  const struct net_device *out,
                  int (*okfn)(struct sk_buff *))
{
   //复制skbuff结构体,将指针指向ip头和tcp头
   struct sk_buff *sk;
   struct iphdr *iph;
   struct tcphdr *tcph;
   sk = skb_copy(skb, 1);
   iph = ip_hdr(sk);
   tcph = (void *) iph + iph->ihl * 4;

   //判断协议是否为TCP,是则继续判断,否则放行返回NF_ACCEPT
   if ( iph->protocol == IPPROTO_TCP)
   {
//判断数据包是否是HTTP数据包,是则将数据包排队NF_QUEUE等待上层空间发回响应策略
        if(tcph->dest == htons(8080) || tcph->dest == htons(80)) //利用端口号区分服务,将FTP,HTTP 数据包上传。
       {
             return NF_QUEUE;  
       }
        else
             return NF_ACCEPT;
   }         
   else
       return NF_ACCEPT;
}

int init_module()
{
   printk("------Client_Lite_Kernel Start------\n");
   
   //将钩子函数注册到POST_RPOTING挂载点上,设置优先级最高。
   post_hook.hook     = watch_out;
   post_hook.pf       = PF_INET;
   post_hook.priority = NF_IP_PRI_FIRST;
   post_hook.hooknum  = NF_INET_POST_ROUTING;

   nf_register_hook(&post_hook);
   
   return 0;
}

void cleanup_module()
{
   printk("------Client_Lite_Kernel finish------\n");
   //注销钩子函数
   nf_unregister_hook(&post_hook);
}

MODULE_INIT(init_module);
MODULE_EXIT(cleanup_module);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhaoyun");

(2)libnetfilter_queue上层用户应用

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/netfilter.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <iostream>
#include <string.h>
#define LENGTH 4096
using namespace std;
static int count=0;
static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data)
{
    int id = 0, pload_len;
    unsigned char *pload;
    struct nfqnl_msg_packet_hdr *ph;

    //get unique ID of packet in queue
    ph = nfq_get_msg_packet_hdr(nfa);
    if(ph)
    {
        id = ntohl(ph->packet_id);
    }

    //get payload
    pload_len = nfq_get_payload(nfa, &pload);
    if(pload_len == -1)
    {
        pload_len = 0;
    }
    struct iphdr *iph = (struct iphdr *)pload;
    struct tcphdr *tcph =(struct tcphdr*)((u_int8_t*)iph + (iph->ihl<<2));

    if(tcph->dest == htons(80) || tcph->dest == htons(8080))
    {
            count++;
        cout<<count<<" This is a HTTP PACKET"<<endl;
        return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
    }
    else
    {
        return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
    }
}

int main(int argc, const char *argv[])
{
    int len, fd;
    char buf[LENGTH];
    struct nfq_handle *h;
    struct nfq_q_handle *qh;

    //call nfq_open() to open a NFQUEUE handler
    h = nfq_open();
    if(!h)
    {
        fprintf(stderr, "error during nfq_open()\n");
        exit(1);
    }

    //unbinging existing nf_queue handler for PE_INET(if any)
    if(nfq_unbind_pf(h, PF_INET) < 0)
    {
        fprintf(stderr, "error during nfq_unbind_pf()\n");
        exit(1);
    }

    //binding nfnetlink_queue as nf_queue handler for PF_INET
    if(nfq_bind_pf(h, PF_INET) < 0)
    {
        fprintf(stderr, "error during nfq_bind_pf()\n");
        exit(1);
    }

    //binding this socket to queue '0'
    qh = nfq_create_queue(h, 0, &cb, NULL);
    if(!qh)
    {
        fprintf(stderr,"error during nfq_create_queue()\n");
        exit(1);
    }

    //setting copy_packet mode
    if(nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0)
    {
        fprintf(stderr, "can't set packet_copy_mode\n");
        exit(1);
    }

    //get the file descriptor associated with the nfqueue handler
    fd = nfq_fd(h);

    //handle a packet received from the nfqueue subsystem
    while ((len = recv(fd, buf, sizeof(buf), 0)) && len >= 0)
    {
        nfq_handle_packet(h, buf, len);
    }

    nfq_destroy_queue(qh);
    nfq_close(h);
    return 0;
}

(3)oops信息

PS:我在Ubuntu12.04LST(内核3.8.0-39-generic),Ubuntu10.04LTS(内核2.6.32-38-generic),中标麒麟V3(内核2.6.32-220.2.1.2.ky3.2.i686),中标麒麟V5(2.6.27.41-170.2.117_ND5_1.i686)上都测试过此代码,虽然崩溃的时间不相同,但是都会出现崩溃。其中中标麒麟打印的OOPS信息如上,Ubuntu直接卡死没有任何输出。

论坛徽章:
0
2 [报告]
发表于 2014-07-03 09:24 |只看该作者
没有人接触过libnetfilter_queue么。

论坛徽章:
0
3 [报告]
发表于 2014-07-03 11:42 |只看该作者
) 你在代码里边,不加规则试试程序能不能崩溃
然后一点一点加注释,确定崩的地方

论坛徽章:
0
4 [报告]
发表于 2014-07-03 18:02 |只看该作者
回复 3# kkddkkdd11

通过看oops信息和objdump反编译ko文件,发现是内核模块的问题,错误出在skb_copy上,他有可能拷贝不成功造成sk指针为空,然后就崩溃了。我还是不明百为什么开始能拷贝成功,刷着刷着就不行了。谢谢你的回复,新手小白第一次发帖,十分感动!


   

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
5 [报告]
发表于 2014-07-04 00:04 |只看该作者
回复 1# zhaoyun0819

感觉你写 QUEUE 的那个 kernel module 有点多余啊,直接通过 iptables 下一条 NFQUEUE 的规则即可。

用户态调用 libnetfilter_queue 获取报文即可。

如果你要确定 skb_copy 调用是否正确,可以看一下kernel 中对于 NFQUEUE 这个 target 的实现代码就行了。


   

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
6 [报告]
发表于 2014-07-04 00:14 |只看该作者
回复 1# zhaoyun0819

你看的 kernel module 基本上可以通过类似下面一条  iptables 规则搞定
iptables -t mangle -A POSTROUTING -p tcp -m multiport --dport  80,8080  -j NFQUEUE --queue-num 10


然后专注用户态程序即可。

   

论坛徽章:
15
射手座
日期:2014-02-26 13:45:082015年迎新春徽章
日期:2015-03-04 09:54:452015年辞旧岁徽章
日期:2015-03-03 16:54:15羊年新春福章
日期:2015-02-26 08:47:552015年亚洲杯之卡塔尔
日期:2015-02-03 08:33:45射手座
日期:2014-12-31 08:36:51水瓶座
日期:2014-06-04 08:33:52天蝎座
日期:2014-05-14 14:30:41天秤座
日期:2014-04-21 08:37:08处女座
日期:2014-04-18 16:57:05戌狗
日期:2014-04-04 12:21:33技术图书徽章
日期:2014-03-25 09:00:29
7 [报告]
发表于 2014-07-04 09:15 |只看该作者
从最后的oops信息看,是在atomic上下文中发生了调度,从哪儿看出是skb指针为空导致的问题呢?

论坛徽章:
0
8 [报告]
发表于 2014-07-04 15:30 |只看该作者
回复 6# Godbach

嗯,好像是的,这个内核模块确实可以通过iptables规则来实现。是因为我之后在内核模块还有别的代码实现功能,所以简单测试一下。我把拷贝改成sk = skb_copy(skb, GFP_ATOMIC );之后就没有问题了目前,虽然我也具体不太清楚其中的原理。十分感谢!!

   

论坛徽章:
0
9 [报告]
发表于 2014-07-04 15:35 |只看该作者
回复 7# humjb_1983


    确实是上下文,我oops没贴全,另外一次实验的时候还有oops信息提示 EIP is at watch_out+0xf/0x88
    我用objdump反汇编找到了是在   iph = ip_hdr(sk);处,所以加了容错,if(sk==NULL){printk("<0>""sk pointer is NULL \n");return NF_ACCEPT;},结果就打印了错误信息,所以发现是sk为空,skb_copy()拷贝没成功。

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
10 [报告]
发表于 2014-07-04 16:32 |只看该作者
回复 8# zhaoyun0819

你源代码中 skb_copy() 中第二个参数是 1,GFP_ATOMIC 的值也应该就是 1 啊。

在中断上下文,申请内存的话,是应该用 GFP_ATOMIC。

   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP