注册 登录
自由的生活_软路由 返回首页

心想事成的个人空间 https://bbs.routerclub.com/?681 [收藏] [复制] [分享] [RSS]

日志

数据包在内核态的捕获、修改和转发(基于netfilter)

已有 1128 次阅读2011-4-8 00:00 |个人分类:内核|

忙活了好几天,经过多次得死机和重启,终于把截获的数据包转发的功能给实现了。同时,也吧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));

这样写,错误!


希望对大家有帮助,欢迎讨论和拍砖!

路过

雷人

握手

鲜花

鸡蛋

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

QQ|Archiver|手机版|小黑屋|软路由 ( 渝ICP备15001194号-1|渝公网安备 50011602500124号 )

GMT+8, 2024-5-6 23:25 , Processed in 0.071998 second(s), 6 queries , Gzip On, Redis On.

Powered by Discuz! X3.5 Licensed

© 2001-2023 Discuz! Team.

返回顶部