忙活了好几天,经过多次得死机和重启,终于把截获的数据包转发的功能给实现了。同时,也吧sk_buff结构学习了一下。
本程序利用netfilter的钩子函数在PREROUTING处捕获数据包,并且修改数据包首部信息,之后直接转发,从而实现对数据包转发得功能。修改数据包得数据和地址之后,最主要的就是对tcp或dp校验和得计算,内核中有相应得函数,但是调用时要明白各个参数所代表得含义。在本程序中,为了验证对skb->data指针的理解,本人还试着对截获的数据包进行了push和pull得调用。现拿出来与大家分享。
author: bbo
kernel: 2.6.31
转载请注明出处。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/socket.h>/*PF_INET*/
#include <linux/netfilter_ipv4.h>/*NF_IP_PRE_FIRST*/
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/inet.h> /*in_aton()*/
#include <net/ip.h>
#include <net/tcp.h>
#define ETHALEN 14
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bbo");
struct nf_hook_ops nfho;
unsigned int checksum(unsigned int hooknum,
struct sk_buff *__skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct sk_buff *skb;
struct net_device *dev;
struct iphdr *iph;
struct tcphdr *tcph;
int tot_len;
int iph_len;
int tcph_len;
int ret;
skb = __skb;
if(skb == NULL)
return NF_ACCEPT;
iph = ip_hdr(skb);
if(iph == NULL)
return NF_ACCEPT;
tot_len = ntohs(iph->tot_len);
if(iph->daddr == in_aton("173.26.100.224"))
{
iph_len = ip_hdrlen(skb);/*in ip.h*/
skb_pull(skb,iph_len);//skb->data指针定位到了传输层
skb_reset_transport_header(skb);/*重置首部长度,现在的首部长度包括了的ip首部长度*/
if(iph->protocol == IPPROTO_TCP)
{
tcph = tcp_hdr(skb);
tcph_len = tcp_hdrlen(skb);
if(tcph->dest == htons(3306)) //根据自己得需求来进行过滤数据包
{
iph->saddr = in_aton("1.2.3.4");
dev = dev_get_by_name(&init_net,"eth0");
tcph->check = 0;
skb->csum = csum_partial((unsigned char *)tcph, tot_len - iph_len,0);
tcph->check = csum_tcpudp_magic(iph->saddr,
iph->daddr,
ntohs(iph->tot_len) - iph_len,iph->protocol,
skb->csum);
iph->check = 0;
iph->check = ip_fast_csum(iph,iph->ihl);
skb->ip_summed = CHECKSUM_NONE;
skb->pkt_type = PACKET_OTHERHOST;
skb->dev = dev;
skb_push(skb,iph_len);/*在返回之前,先将skb中得信息恢复至原始L3层状态*/
//skb_reset_transport_header(skb);
skb_push(skb, ETHALEN);//将skb->data指向l2层,之后将数据包通过dev_queue_xmit()发出
ret = dev_queue_xmit(skb);
if(ret < 0)
{
printk("dev_queue_xmit() error\n");
goto out;
}
return NF_STOLEN;
}
}
skb_push(skb,iph_len);/*在返回之前,先将skb中得信息恢复至原始L3层状态*/
skb_reset_transport_header(skb);
}
return NF_ACCEPT;
out:
dev_put(dev);
//free(skb);
return NF_DROP;
}
static int __init filter_init(void)
{
int ret;
nfho.hook = checksum;
nfho.pf = AF_INET;
nfho.hooknum = NF_INET_PRE_ROUTING;
nfho.priority = NF_IP_PRI_FIRST;
ret = nf_register_hook(&nfho);
if(ret < 0)
{
printk("%s\n", "can't modify skb hook!");
return ret;
}
return 0;
}
static void filter_fini(void)
{
nf_unregister_hook(&nfho);
}
module_init(filter_init);
module_exit(filter_fini);
|
本程序是利用了截获得数据包进行得实验,由于数据包中得有原始mac,所以未对数据包得mac进行修改操作。其实,在调用
dev_queue_xmit(skb)函数前,
大家应该构造得是一个完整得skb,即应该根据需要来对mac进行修改,不过,不需要对L2层进行校验计算。大家如果需要就根据此程序进行修改吧,不算麻烦。
写程序时我还有一点不明白,就是计算tcp得校验和时
tcph->check = 0;
skb->csum = csum_partial((unsigned char *)tcph, tot_len - iph_len,0);
tcph->check = csum_tcpudp_magic(iph->saddr,
iph->daddr,
ntohs(iph->tot_len) - iph_len,iph->protocol,
skb->csum)
这样写,正确!
|
而另一种计算方式却时错误,如下。如果大家有明白原因的请指点,谢谢!
skb->csum = csum_partial((unsigned char *)(tcph + tcph_len) , tot_len - iph_len - tcph_len ,0);
tcph->check = 0;
tcph->check = csum_tcpudp_magic(iph->saddr,
iph->daddr,
ntohs(iph->tot_len) - iph_len,iph->protocol,
csum_partial( (unsigned char *)tcph,
tcph_len, skb->csum));
这样写,错误!
|
希望对大家有帮助,欢迎讨论和拍砖!
阅读(2941) | 评论(0) | 转发(0) |