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

http://biancheng.dnbcw.info/linux/263145.html

忙活了好几天,经过多次得死机和重启,终于把截获的数据包转发的功能给实现了。同时,也吧sk_buff结构学习了一下。

    本程序利用netfilter的钩子函数在PREROUTING处捕获数据包,并且修改数据包首部信息,之后直接转发,从而实现对数据包转发得功能。修改数据包得数据和地址之后,最主要的就是对tcp或dp校验和得计算,内核中有相应得函数,但是调用时要明白各个参数所代表得含义。在本程序中,为了验证对skb->data指针的理解,本人还试着对截获的数据包进行了push和pull得调用。现拿出来与大家分享。
   author: bbo
   kernel: 2.6.31
   转载请注明出处。

#include
#include
#include
#include /*PF_INET*/
#include /*NF_IP_PRE_FIRST*/
#include
#include
#include /*in_aton()*/
#include
#include
#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));

       这样写,错误!

你可能感兴趣的:(网络,linux,相关)