忙活了好几天,经过多次得死机和重启,终于把截获的数据包转发的功能给实现了。同时,也吧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));
这样写,错误!
|
希望对大家有帮助,欢迎讨论和拍砖! | | |