`
garfieldcatcat
  • 浏览: 6663 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

在内核中构造一个数据包,让指定的网卡来接收。

阅读更多
网上在内核中构造一个数据包然后通过一个网络接口发送出去的例子有很多,但是在内核中构造一个数据包,让网络接口来接收的方法的文章不多。
文章参考了:http://bbs.chinaunix.net/thread-1931661-1-1.html,谢谢该文的作者以及回帖者。
本人经历的项目中有个很奇怪的需求。主机有2个网络接口,要求从一个网络接口接收数据,内核对收到的数据进行修改等操作,然后由另外一个网络接口来接收这些修改后的数据。最基本的想法就是调用netif_rx函数来实现,但是本人以前一直没有成功的调用netif_rx函数,调用时出现死机,直到遇到上面那篇文章。
参考代码如下:
// eth1是客户端的网关,eth0是进行模拟接收的适配器。

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/inet.h>
#include <linux/netdevice.h> 
#include <linux/inetdevice.h> 
#include <linux/if_ether.h> 
#include <linux/if_packet.h>
#include <linux/netfilter_ipv4.h>
#include <linux/param.h>
#include <linux/completion.h>       	// for DECLARE_COMPLETION()
#include <linux/sched.h>            	// for daemonize() and set_current_state()
#include <linux/delay.h> 
#include <linux/in.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/etherdevice.h>

#include <net/ip.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/dst.h>

//
MODULE_DESCRIPTION("My kernel module");
MODULE_AUTHOR("root (root@localhost.localdomain)");
MODULE_LICENSE("Dual BSD/GPL");
//


// 配置参数
unsigned int ETH0= 0;
unsigned int MASK0= 0;
unsigned int ETH1= 0; 
unsigned int MASK1= 0; 


// 设备
struct net_device *dev0 =0;
struct net_device *dev1 =0;
struct in_device *indev0=0;
struct in_device *indev1=0;
struct in_ifaddr *ifr0  =0;
struct in_ifaddr *ifr1  =0;

// 钩子处理函数
unsigned int sendip(unsigned int hook,
			   struct sk_buff **pskb,
			   const struct net_device *in,
			   const struct net_device *out,
			   int (*okfn)(struct sk_buff *))
{
	// DEBUG
	printk(KERN_EMERG "send_ip \n");
	return NF_ACCEPT;
}

// 钩子处理函数
unsigned int getip(unsigned int hook,
			   struct sk_buff **pskb,
			   const struct net_device *in,
			   const struct net_device *out,
			   int (*okfn)(struct sk_buff *))
{
	int ret;
	struct ethhdr *peth=NULL; 
	struct iphdr *pip=NULL;
	struct iphdr *p=NULL;
	struct sk_buff *skb;

	if(pskb==NULL)
		return NF_DROP; 

	if((*pskb)==NULL)
		return NF_DROP; 
	
	// (*pskb)->len是IP包的长度(包括IP头)

	if(memcmp(in->name,"eth0",4)==0)
	{
		
	}else
	{
		if(pip->daddr == ETH0)
		{
			// 让eth0来接收
			skb = alloc_skb((*pskb)->len + 2 + ETH_HLEN,GFP_ATOMIC); // 新的IP包 
			skb_reserve(skb,2);
			skb_put(skb,(*pskb)->len + ETH_HLEN);

			memcpy(skb->data,(*pskb)->data - ETH_HLEN,(*pskb)->len + ETH_HLEN);

			skb->mac.raw = skb->data;
			skb->mac_len = ETH_HLEN;
			skb->len = (*pskb)->len + ETH_HLEN;
			skb->ip_summed = CHECKSUM_NONE;
			skb->pkt_type = PACKET_HOST;
			skb->protocol = __constant_htons(ETH_P_IP);
			skb->dev = dev0;

			// 关键,以前没有成功就是下面的参数没有设置。
			skb_shinfo(skb)->nr_frags=0;
			skb_shinfo(skb)->frag_list=NULL;
			skb_shinfo(skb)->frags[0].page=NULL;

			skb_pull(skb,ETH_HLEN);
			skb_reset_network_header(skb);
			
			ret=netif_rx(skb);
			dev0->last_rx = jiffies;

			//printk(KERN_EMERG "ret=%d , %d \n",ret,gi++);
			return NF_STOLEN;
		}
	}
	return NF_ACCEPT;
}

struct nf_hook_ops pre_ops =
{
	.hook = getip,
	.pf = PF_INET,
	.hooknum = NF_IP_PRE_ROUTING,
	.priority = NF_IP_PRI_FIRST,
};

struct nf_hook_ops post_ops =
{
	.hook = sendip,
	.pf = PF_INET,
	.hooknum = NF_IP_LOCAL_OUT,
	.priority = NF_IP_PRI_FIRST,
};

static int gatekernel_init_module(void)
{
	printk(KERN_EMERG "Module Init.\n");
	
	// 初始化设备对象
	dev0 = dev_get_by_name("eth0");
	if(!dev0)
	{
		printk( KERN_EMERG "Counld not get dev0.\n");
		return -1;
	}
	ETH0 = inet_select_addr(dev0,0,RT_SCOPE_UNIVERSE);
	indev0 = (struct in_device*)dev0->ip_ptr;
	ifr0= indev0->ifa_list;
	MASK0 = ifr0->ifa_mask;
	
	dev1 = dev_get_by_name("eth1");
	if(!dev1)
	{
		printk( KERN_EMERG "Counld not get dev1.\n");
		return -1;
	}
	ETH1 = inet_select_addr(dev1,0,RT_SCOPE_UNIVERSE);
	indev1 = (struct in_device*)dev1->ip_ptr;
	ifr1= indev1->ifa_list;
	MASK1 = ifr1->ifa_mask;

	nf_register_hook(&pre_ops);
	nf_register_hook(&post_ops);

	return 0;
}

// 反初始化
static void gatekernel_exit_module(void)
{
	// 取消钩子函数
	nf_unregister_hook(&pre_ops);
	nf_unregister_hook(&post_ops);

	printk(KERN_EMERG "bye.\n");
}

module_init(gatekernel_init_module);
module_exit(gatekernel_exit_module);


原帖的内容是:
正在做一个项目 里面需要有这样一个步骤

1.在网络层将数据包截住并修改
2.重新把修改后的数据包交付给协议栈底端 让它重新经过协议栈

开始阶段 为了实现简单 用ICMP报文作为拦截对象 且截住的数据包不做过多修改 只将协议头部若干指针修改 让它的报文头状态变为好像刚刚从网卡到来的时候一样

实现的时候 用的是netfilter的PRE_ROUTING钩子 检查每一个数据包 如果是ICMP报文且类型为8 即ECHO报文 就将报文截获 复制一个 修改头部信息 然后重新调用netif_rx 让协议栈接收 原来的报文使用NF_STOLEN处理

注册在PRE_ROUTING钩子处的回调函数如下


static unsigned int pre_routing_hook(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, \
                const struct net_device *out, int (*okfn)(struct sk_buff *))
{
        struct sk_buff *skb;
        struct sk_buff *new_skb;
        //unsigned char src_ip[4];
        //unsigned char dst_ip[4];
        //
        struct icmphdr *icmp_hdr;
        int icmp_hdr_off;
        //
        struct iphdr *ip_hdr;
        int ip_hdr_off;
        struct ethhdr *eth_hdr;
        
        struct net_device *NIC = NULL;
        const char nic[5] = "eth0";
        // printk(KERN_INFO "inside the filter\n");
        
        int index;
        
        int rx_ret;
        
        skb = skb_copy(*pskb, GFP_ATOMIC);
        NIC = dev_get_by_name(nic);
        
        if (!NIC) {
                return NF_ACCEPT;
        }
        
        if (skb->nh.iph->protocol == IPPROTO_ICMP) {
                // get ip header
                ip_hdr = (struct iphdr *)skb->nh.iph;
                // calcutate ip header length
                ip_hdr_off = ip_hdr->ihl << 2;
                
                // get icmp header
                icmp_hdr = (struct icmphdr *)(skb->data + ip_hdr_off);
                                                
                // only modify icmp request
                // #define ICMP_ECHO        8        /* Echo Request */
                // in <linux/icmp/h>
                if (icmp_hdr->type == ICMP_ECHO) {
                        new_skb = alloc_skb(skb->len + 5 + ETH_HLEN, GFP_ATOMIC);
                        skb_reserve(new_skb, 2);
                        skb_put(new_skb, skb->len + ETH_HLEN);
                        memcpy(new_skb->data, skb->data - ETH_HLEN, skb->len + ETH_HLEN);
                        new_skb->mac.raw = new_skb->data;
                        // commented
                        //skb_pull(new_skb, ETH_HLEN);
                        
                        new_skb->mac_len = ETH_HLEN;
                        new_skb->len = skb->len + ETH_HLEN;
                        
                        new_skb->ip_summed = CHECKSUM_UNNECESSARY;
                        new_skb->pkt_type = PACKET_HOST;
                        new_skb->protocol = htons(ETH_P_IP);
                        new_skb->dev = NIC;
                        skb_shinfo(new_skb)->nr_frags = 0;
                        skb_shinfo(new_skb)->frag_list = NULL;
                        skb_shinfo(new_skb)->frags[0].page = NULL;
                        
                        rx_ret = netif_rx(new_skb);
                        NIC->last_rx = jiffies;
                        kfree_skb(skb);
                        return NF_STOLEN;
                }
        }
        kfree_skb(skb);
        return NF_ACCEPT;
}
0
0
分享到:
评论

相关推荐

    android混杂模式:监听网卡数据demo

    在Android平台上,混杂模式(Promiscuous Mode)是一种特殊的数据包捕获方式,它允许设备接收到网络上的所有数据包,而不仅仅是发往或来自自身设备的包。这种技术通常用于网络安全分析、故障排查或者开发网络应用。...

    网络编程中如何使用raw socket发送magic packet网络唤醒数据包实现远程唤醒

    3. 打开raw socket:在Linux下,使用`socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))`创建一个raw socket,其中`AF_PACKET`表示使用原始套接字,`SOCK_RAW`表示获取原始数据包,`htons(ETH_P_ALL)`表示接收所有类型...

    原始套接字 原始套接字

    此外,还需要掌握如何生成和验证校验和,这是IP协议中的一个重要安全机制。通过深入理解原始套接字的工作原理及其在网络安全中的应用,开发者可以更好地理解和应对各种网络攻击,同时也能开发出更为复杂的网络工具和...

    linux_sock_raw原始套接字编程

    在数据包的传输过程中,当一个14+20+8+100+4字节的UDP以太网数据帧到达网卡时,网卡会根据其配置(如是否启用混杂模式)进行初步过滤。如果数据帧通过了硬过滤,它将被传递给IP输入例程。在IP层,系统会再次过滤...

    论文研究-基于NDIS的自组织网测试床设计与实现.pdf

    多跳数据传输机制是自组织网的核心功能之一,它要求网络中的每个节点都能有效地转发数据包,这通常需要一个良好的路由协议来支持。路由表的管理则是确保数据能够正确地按预定路径转发的关键,它包括路由表的查找、...

    使用winpcap进行网络开发

    6. **pcap_sendpacket()**:向网络发送一个自定义数据包。 **三、链路层通信与文件传输** 1. **链路层通信**:在TCP/IP模型中,链路层负责数据帧的传输,Winpcap可以直接操作这一层,实现网卡到网卡的数据传输。...

    linux网卡驱动的关键就是处理两个问题

    此外,为了更好地理解接收流程,我们可以模拟一个简单的定时器驱动,定期构造`sk_buff`并提交给上层,尽管这在实际应用中并无太大意义。 当存在**多个网卡共享同一驱动**的情况时,驱动程序需要具备智能路由的能力...

    异构网络DPDK实施方案转发1

    在【实施用途环境示意图】中,我们看到一个包含多个元素的通信环境,包括`terminal_send`(移动通信车)作为发送端,`terminal_recv`(服务器)作为接收端,以及多个路由器(router1到router6)和接入点(Access_...

    计算机网络课程设计-监控IP包流量

    - **任务**: 编写一个程序来监控网络流量,捕捉一定时间内网络上的IP数据包,并根据源地址统计每个源地址在此期间发送的IP包数量。最终结果可以通过日志文件记录下来,或者以图形的方式展示。 - **具体要求**: - ...

    ARM嵌入式系统开发发送过程的实现

    这里 `struct sk_buff` 是 Linux 网络协议栈中的一个重要数据结构,用于在网络堆栈的不同层之间传递数据包。 2. **检查缓冲区分配**:函数会检查 DM9000 芯片内部的缓冲区是否已成功分配。如果分配成功,则继续进行...

    tcp迁移报告tcp迁移报告

    Netfilter是Linux内核中的一个框架,用于对网络数据包进行过滤、修改或重定向。它通过一系列的HOOK点来拦截数据包,并允许用户自定义处理函数来实现特定的功能。 - **Netfilter框架的结构和功能** - Netfilter框架...

    IT书籍Linux设备驱动程序设计.ppt

    网络接口设备则负责数据包的发送和接收,如以太网卡或回环接口。 在Linux系统中,设备由主设备号和次设备号唯一标识,主设备号相同意味着它们使用相同的驱动程序,次设备号用于区分同一类型的不同设备实例。设备...

    TRex Statelss使用说明

    2. **创建目录**:在服务器上创建一个名为“trex”的目录。 3. **下载并安装TRex**:使用`wget`命令下载TRex安装包并进行解压。 通过上述详细介绍,我们可以看出TRex不仅具备强大的发包能力,还具有很高的灵活性和...

    packet11:Packet11驱动程序的反向工程源代码(适用于Windows的802.11 Packet Injection)-windows source code

    Packet11 驱动程序是专为 Windows 操作系统设计的,它允许用户在802.11无线网络中发送和接收原始数据包,从而实现数据包注入。Packet11 的源代码逆向工程揭示了其内部工作原理和技术细节,对于学习无线网络安全和...

    raspi-wifimon:使用在RasPi上运行的ScaPy的WiFi监视器

    主要代码将使用Scapy库来初始化无线网卡到monitor模式,创建一个嗅探器,然后持续监听和处理接收到的数据包。开发者可能会使用Scapy的`recv()`函数来接收数据包,`Ether()`和`Dot11`类来解析以太网和802.11帧,以及`...

Global site tag (gtag.js) - Google Analytics