- 论坛徽章:
- 0
|
各位大神,我想完成一个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直接卡死没有任何输出。
|
|