Chinaunix首页 | 论坛 | 博客
  • 博客访问: 41979
  • 博文数量: 9
  • 博客积分: 1557
  • 博客等级: 上尉
  • 技术积分: 124
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-06 17:06
文章分类
文章存档

2011年(2)

2010年(7)

我的朋友

分类: LINUX

2010-10-16 14:08:19

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

       这样写,错误!

   
    希望对大家有帮助,欢迎讨论和拍砖!
阅读(6292) | 评论(4) | 转发(1) |
给主人留下些什么吧!~~

Hey_Lightman2016-09-18 12:00:35

trace在这了:
Sep 14 18:18:55 localhost kernel: slen = 2960 | sdata_len = 2920
Sep 14 18:18:55 localhost kernel: ------------[ cut here ]------------
Sep 14 18:18:55 localhost kernel: WARNING: at net/core/dev.c:1907 skb_warn_bad_offload+0xc2/0xf0() (Tainted: G        W  ---------------   )
Sep 14 18:18:55 localhost kernel: Hardware name: 

Hey_Lightman2016-09-18 11:57:54

楼主,您好,我参照你的这篇文章,写了一份代码,我用的是nskb = skb_copy(skb, GFP_ATOMIC)复制出一份nskb,但是我发现skb->len和nskb->len相同,但是skb->data_len有值,copy出来的nskb->data_len却为0,这个地方十分不理解,然后我还发现,当每次当原来的skb->data_len为2920(nskb->data_len始终为0)的时候,内核会有警告的trace,trace的内容如下,麻烦帮忙看看,自己确实没看懂,谢啦

LYZC112011-10-18 14:44:59

帅哥,你这段代码是要加在内核源码的哪里啊?怎么编译啊

LYZC112011-10-17 17:02:00

版主,你这个程序编译时放置的路径是哪里啊?