内核过滤数据包,第一个想到的是iptables,这个东西是用户层的, 深入点就是netfilter了。 netfilter的5个钩子点可以实现这个。
对内核熟悉点的人会知道layer7, 有的使用框架snort,layer7已经不更新,继承者是ipp2p, 这些都可以。还有Libpcap不知道怎么做,目前没有去深入研究过。
我的本意是针对含有特殊字符串的数据包重定向端口,其他的数据包直接发送到网络上。
但是实际情况如何,目前我也没时间移植到系统是,只是在本地做了实验,原理上是可以实现的。
#include
#include
#include
#include
#include
#include
#define DRIVER_AUTHOR "AlexKey"
#define DRIVER_DESC "HTTP packets manipulations"
#define DEBUG 1
static struct nf_hook_ops nfho;
static 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 *)){
struct iphdr *ip_header;
struct tcphdr *tcp_header;
struct ethhdr *eth_header;
u32 saddr, daddr;
u16 source, dest;
int i = 0;
int found = 0;
struct ts_config *conf;
struct ts_state state;
const char *pattern = "GET / HTTP/1.1";
conf = textsearch_prepare("kmp", pattern, strlen(pattern),
GFP_KERNEL, TS_AUTOLOAD);
if (IS_ERR(conf))
{
//err = PTR_ERR(conf);
return NF_ACCEPT;
}
/* Get all the headers */
eth_header = (struct ethhdr *)skb_mac_header(skb);
ip_header = (struct iphdr *)skb_network_header(skb);
skb_set_transport_header(skb, ip_header->ihl * 4);
tcp_header = (struct tcphdr *)skb_transport_header(skb);
/* If the packet source or dest are not 80 then the packet is not for us :) */
//if(tcp_header->source != ntohs(80) && tcp_header->dest != ntohs(80))
if(tcp_header->source == ntohs(80))
{
printk(KERN_INFO "Recv skb_len=%d, data_len=%d, truesize=%d\n", skb->len, skb->data_len,
skb->truesize);
//if (skb->data_len > 0)
{
printk(KERN_INFO "\n[%s]\n", skb->data);
}
return NF_ACCEPT;
}
else if (tcp_header->dest == ntohs(80) && found == 0)
{
printk(KERN_INFO "Send skb_len=%d, data_len=%d, truesize=%d\n", skb->len, skb->data_len,
skb->truesize);
/*if (skb->len > 0)
{
for (i=0; i < skb->len; i++)
{ //Below is 'GE' from "GET / HTTP/1.1"
if (0x47 == *(skb->data+i) && 0x45 == *(skb->data+i + 1))
{
printk(KERN_INFO "===========Found GET===1==========\n");
found = 1;
}
if (0x45 == *(skb->data+i) && 0x47 == *(skb->data+i + 1))
{
printk(KERN_INFO "===========Found GET===2==========\n");
return NF_ACCEPT;
}*/
//printk(KERN_INFO "%x ", *(skb->data+i));
//if (i % 8 == 0)
// printk("\nline=[%d]", i);
if (UINT_MAX != skb_find_text(skb, 0, skb->len, conf, &state))
{
printk(KERN_INFO "skb_find_text find the test\n");
//break;
}
else {
printk(KERN_INFO "Not found anything\n");
}
//textsearch_destroy(conf);
//}
//}
return NF_ACCEPT;
}
else
{
printk(KERN_INFO "dest (%d) skb_len=%d, data_len=%d, truesize=%d\n", tcp_header->dest, skb->len, skb->data_len,
skb->truesize);
return NF_ACCEPT;
}
#if DEBUG > 0
printk(KERN_INFO "[HTTP] Got packet on %d from %d\n", htons(tcp_header->dest), htons(tcp_header->source));
#endif
saddr = ip_header->saddr;
daddr = ip_header->daddr;
source = tcp_header->source;
dest = tcp_header->dest;
/* In link layer header change sender mac to our ethernet mac
and destination mac to sender mac :) ping-pong */
memcpy(eth_header->h_dest,eth_header->h_source,ETH_ALEN);
memcpy(eth_header->h_source,skb->dev->dev_addr,ETH_ALEN);
/* Set new link layer headers to socket buffer */
skb->data = (unsigned char *)eth_header;
skb->len += ETH_HLEN;
/* Setting it as outgoing packet */
skb->pkt_type = PACKET_OUTGOING;
/* Swap the IP headers sender and destination addresses */
memcpy(&ip_header->saddr, &daddr, sizeof(u32));
memcpy(&ip_header->daddr, &saddr, sizeof(u32));
/* If transmission suceeds then report it stolen
if it fails then drop it */
if(dev_queue_xmit(skb)==NET_XMIT_SUCCESS){
#if DEBUG > 0
printk(KERN_INFO "[HTTP] Successfully sent packet\n");
#endif
return NF_STOLEN;
} else {
#if DEBUG > 0
printk(KERN_INFO "[HTTP] Sending failed\n");
#endif
return NF_DROP;
}
}
static int __init init_main(void) {
nfho.hook = hook_func;
//nfho.hooknum = 0;
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST;
nfho.hooknum = NF_INET_POST_ROUTING; /*NF_INET_PRE_ROUTING;*/
nf_register_hook(&nfho);
#if DEBUG > 0
printk(KERN_INFO "[HTTP] Successfully inserted protocol module into kernel.\n");
#endif
return 0;
}
static void __exit cleanup_main(void) {
nf_unregister_hook(&nfho);
#if DEBUG > 0
printk(KERN_INFO "[HTTP] Successfully unloaded protocol module.\n");
#endif
}
module_init(init_main);
module_exit(cleanup_main);
MODULE_LICENSE("GPL v3");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
初始化,是采用检测发送POSTROUTING 锚点,
一开始是通过对比二进制数据GET判断这个请求是什么。
后来找到了更高级的函数skb_find_text(). 非常实用, 由这个函数来寻找数据。
对于目的端口不是80的数据则通过dev_queue_xmit(skb)直接发送。
源码地址qianguozheng