hl626818 发表于 2016-03-31 09:55

利用netfilter在内核修改IP地址并转发---求教!!!

首先说明问题:
三台机器相连A--B---C,中间一台B作为A,C的默认网关。IP地址分别为:10.1.1.3----10.1.1.1 / 169.1.1.3---169.1.1.1。
利用下面的程序在B截获从A-->C的包,并对截获的包进行目的ip地址和源ip地址修改,校验和后发出。
现象:利用wireshark观察,B主机的wireshark可以截获到2个包,一个输入的一个地址转化后的,都没有错误提示。但是在C却收到的是以下信息的包:
对比B和C的wireshark发现,只有一个地方不一直:在B中是0800 但传到C就成了0004了。。。。。。。求指导!!!谢谢谢谢!!!
#include<linux/init.h>
#include<linux/module.h>
#include<linux/netfilter.h>
#include<linux/netfilter_ipv4.h>
#include<linux/socket.h>
#include<linux/skbuff.h>
#include<linux/netdevice.h>
#include<linux/inet.h>
#include<net/ip.h>
#include<net/tcp.h>

#define ETHALEN 14
#define ETH_ALEN 6
MODULE_LICENSE("GPL");
MODULE_AUTHOR("123");

//#define ETH_P_803 0x0001
#define ETH_P_IP 0x0800
unsigned char src_mac1 = {0x00,0x24,0x7E,0x0E,0x0A,0x8A}; //169.1.1.3
unsigned char dst_mac1 = {0xEC,0x88,0x8F,0xEA,0x4D,0xAC}; //169.1.1.1
//unsigned char src_mac2 = {};
//unsigned char dst_mac2 = {};

struct nf_hook_ops nfho;

static 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 ethhdr *eth_out;
        //struct ethhdr *eth_in;

        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("192.168.137.1")) //查看目的IP地址!对是本IP地址的包进行以下操作
        {
                iph_len = ip_hdrlen(skb); //获取ip首部长度
                skb_pull(skb,iph_len); //skb->data指针定位到传输层
                skb_reset_transport_header(skb);//重置首部长度,现在的首部长度包括了的ip首部长度??
                if(iph->protocol == IPPROTO_TCP) //TCP协议
                {
                        tcph = tcp_hdr(skb);
                        tcph_len = tcp_hdrlen(skb);
                        if(tcph->dest==htons(8888)) //根据自己的需求来进行过滤数据包
                        {
                                iph->saddr = in_aton("169.1.1.3"); //修改源IP地址(本地的一个IP地址)
                                iph->daddr = in_aton("169.1.1.1"); //修改目的IP地址(与本机相连的一个主机的IP地址)
                                dev = dev_get_by_name(&init_net,"eth1"); //获取输出dev信息,这里eth1 对应的是169.1.1.3的..
                                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;
                                ip_send_check(iph);
                                skb->ip_summed = CHECKSUM_NONE;
                                skb->pkt_type = PACKET_OTHERHOST;
                                skb->dev = dev;
                                //skb->protocol = ETH_P_803;
                                skb_push(skb,iph_len);        //在返回之前,先将skb中的信息恢复至原始L3层状态
                                //skb_reset_transport_header(skb);
                                eth_out = (struct ethhdr *)skb_push(skb,ETHALEN);//将skb->data指向l2层
                                eth_out->h_proto= ETH_P_IP;                        //设置MAC中的 类型
                                memcpy(eth_out->h_dest,src_mac1,ETH_ALEN);        //设置MAC中的 目的IP地址
                                memcpy(eth_out->h_source,dst_mac1,ETH_ALEN);        //设置MAC中的 源IP地址

                                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 = (nf_hookfn*)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);

hl626818 发表于 2016-03-31 11:14

真心求教啊,有点着急~~~~~~~~大神救救我吧

hl626818 发表于 2016-03-31 19:03

自己再顶一下~~:em16:

Godbach 发表于 2016-03-31 19:14

回复 1# hl626818

tcph = tcp_hdr(skb);

数据包在 IP 层的时候,调用这个接口,你要确认一下是否真的是 tcp 的 header。看一下 tcp_hdr 的代码吧。

根据很早的老经验,这个接口在 IP 层调用的话,返回的 ip hdr。{:qq8:}


   

hl626818 发表于 2016-03-31 20:48

首先谢谢您的回复。
虽然还没测试,但应该不会由问题,因为在tcph = tcp_hdr(skb)后面对端口号的判断可以正确实现:对地址的转换都能完成,本机能转发数据,另一台机器能收到....只是收到的有问题。
不知道为啥子另一台机器的protocol变成了0x0004??额 好像发现了什么...我去查查资料
回复 4# Godbach


   

hl626818 发表于 2016-04-05 15:40

好吧,自己来回答: 虽然没有验证,但应该是79行的字节序出错了。 由于是截获包转发包,所以向以太网首部中填充类型字段完全没有必要(截获的包自己就有),所以我就将79屏蔽掉。
然后就正常了......再次感慨一下:自己一个人调程序真TM痛苦
页: [1]
查看完整版本: 利用netfilter在内核修改IP地址并转发---求教!!!