目的:使虚拟网卡能ping通3.3.3.4
方法:由于是虚拟网卡,没有与真实网络连接,为了ping通,可以在驱动程序中构造一个函数,这个函数的作用是修改网卡发出去的报文,把报文中的目标mac和源mac对调,把目标ip和源ip对调,等等,假装这个修改后的报文就是接收到的报文,通过netif_rx上报,系统接收到这个上报的报文后,由于格式和3.3.3.4的回复报文的格式一样,系统以为这就是3.3.3.4回复的报文,显示ping通了
代码如下
-
/*
-
* 参考drivers\net\dm9000.c
-
*/
-
#include <linux/module.h>
-
#include <linux/ioport.h>
-
#include <linux/netdevice.h>
-
#include <linux/etherdevice.h>
-
#include <linux/init.h>
-
#include <linux/interrupt.h>
-
#include <linux/skbuff.h>
-
#include <linux/spinlock.h>
-
#include <linux/crc32.h>
-
#include <linux/mii.h>
-
#include <linux/ethtool.h>
-
#include <linux/dm9000.h>
-
#include <linux/delay.h>
-
#include <linux/platform_device.h>
-
#include <linux/irq.h>
-
#include <linux/slab.h>
-
#include <linux/ip.h>
-
-
#include <asm/delay.h>
-
#include <asm/irq.h>
-
#include <asm/io.h>
-
-
static struct net_device *vnet_dev;
-
-
static void emulate_receive_package(struct sk_buff *skb, struct net_device *dev)
-
{
-
/* 参考LDD3 */
-
unsigned char *type;
-
struct iphdr *ih;
-
__be32 *saddr, *daddr, tmp;
-
unsigned char tmp_dev_addr[ETH_ALEN];
-
struct ethhdr *ethhdr;
-
-
struct sk_buff *rx_skb;
-
-
// 从硬件读出/保存数据
-
/* 对调"源/目的"的mac地址 */
-
ethhdr = (struct ethhdr *)skb->data;
-
memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN);
-
memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN);
-
memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN);
-
-
/* 对调"源/目的"的ip地址 */
-
ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));
-
saddr = &ih->saddr;
-
daddr = &ih->daddr;
-
-
tmp = *saddr;
-
*saddr = *daddr;
-
*daddr = tmp;
-
-
//((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
-
//((u8 *)daddr)[2] ^= 1;
-
type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);
-
//printk("tx package type = %02x\n", *type);
-
// 修改类型, 原来0x8表示ping
-
*type = 0; /* 0表示reply */
-
-
ih->check = 0; /* and rebuild the checksum (ip needs it) */
-
ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
-
-
// 构造一个sk_buff
-
rx_skb = dev_alloc_skb(skb->len + 2);
-
skb_reserve(rx_skb, 2); /* align IP on 16B boundary */
-
memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
-
-
/* Write metadata, and then pass to the receive level */
-
rx_skb->dev = dev;
-
rx_skb->protocol = eth_type_trans(rx_skb, dev);
-
rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
-
dev->stats.rx_packets++;
-
dev->stats.rx_bytes += skb->len;
-
-
// 提交sk_buff
-
netif_rx(rx_skb);
-
}
-
-
static int vnet_send_packet(struct sk_buff *skb, struct net_device *dev)//发包函数
-
{
-
/* 如果是真实的网卡:
-
* 1. 停止网卡的队列: netif_stop_queue(dev);
-
* 2. 把skb的数据移到网卡的内存去
-
* 3. 释放skb
-
* 4. 数据真正发送出去之后产生中断
-
* 5. 在中断处理函数里:
-
* 更新网卡的统计信息
-
* netif_wake_queue(dev);
-
*/
-
-
static int cnt = 0;
-
printk("vnet_send_packet %d\n", ++cnt);
-
-
netif_stop_queue(dev);//停止网卡的队列
-
-
/* 把skb的数据移到网卡的内存去,我们是虚拟网卡,没有这一步 */
-
-
/* 构造一个假的sk_buff,上报 */
-
emulate_receive_package(skb, dev);
-
-
dev_kfree_skb(skb);//释放skb
-
-
/*真实网卡的以下信息应该在中断处理函数中完成*/
-
/*更新统计信息*/
-
dev->stats.tx_bytes += skb->len;//发送的字节数
-
dev->stats.tx_packets++;//发送的包个数
-
-
netif_wake_queue(dev);//数据全部发送出去后,唤醒网卡队列
-
-
-
-
return 0;
-
}
-
-
static struct net_device_ops vnet_dev_ops =
-
{
-
/* 2.1 在结构体net_device_ops中定义发包函数:nodo_start_xmit */
-
.ndo_start_xmit = vnet_send_packet,
-
};
-
-
static int vnet_init(void)
-
{
-
/* 1. 分配一个net_device */
-
vnet_dev = alloc_netdev(0, "vnet%d", ether_setup); // alloc_etherdev;
-
-
/* 2. 设置: */
-
/* 2.2 其他信息 */
-
/*设置mac地址*/
-
vnet_dev->dev_addr[0] = 0x00;
-
vnet_dev->dev_addr[1] = 0xE0;
-
vnet_dev->dev_addr[2] = 0x4A;
-
vnet_dev->dev_addr[3] = 0xbc;
-
vnet_dev->dev_addr[4] = 0x15;
-
vnet_dev->dev_addr[5] = 0xe8;
-
-
/* 设置下面这项才能ping通 */
-
vnet_dev->flags |= IFF_NOARP;
-
-
/* 3. 硬件相关的: request_irq, 在中断函数里得到数据后用netif_rx上报 */
-
-
/* 4. 注册net_device */
-
vnet_dev->netdev_ops = &vnet_dev_ops;//必须添加,否则会注册失败
-
register_netdev(vnet_dev);
-
-
-
return 0;
-
}
-
-
static void vnet_exit(void)
-
{
-
unregister_netdev(vnet_dev);
-
free_netdev(vnet_dev);
-
}
-
-
module_init(vnet_init);
-
module_exit(vnet_exit);
-
-
MODULE_LICENSE("GPL");
阅读(1576) | 评论(0) | 转发(0) |