📄 vmeth.c
字号:
/************************************************************************
虚拟多网卡驱动程序.原理:
接收的原理:
1.通过dev_add_pack注册一个原始以太网接收接口模拟接收报文,所以被绑定的网卡要打开侦听模式;
2.在本程序里面使用一个模拟网卡的数组,最多可以模拟1024个网卡;
3.根据报文的目的MAC地址,使用HASH算法计算该报文是所模拟的哪个网卡的报文;
4.通过netif_rx()函数将报文加入到接受队列中.
发送的原理:
1.在实现hard_start_xmit函数时,将保文直接通过被绑定的网卡的hard_start_xmit函数发送出去.
HASH算法的原理:
HASH = 将报文目的MAC的低10位.HASH值相同的网卡链成单向链表.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version
2 of the License, or (at your option) any later version.
作者:陈国强, douspring@263.net
**************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include "vmeth.h"
struct net_local{
struct net_device_stats stats;
unsigned short usIndex;
};
VMETH_HASH_ITEM_S *gstHashTable[VMETH_HASHTABLE_LEN];
struct net_device gstDevTable[VMETH_HASHTABLE_LEN];
struct net_device *gpstHostDev = NULL;
static int card_num = 1;
static char *host_dev = VMETH_DEF_BOUND_ETH;
static int base_mac[6]= DEF_BASEMAC;
/*static char base_mac[32]= DEF_BASEMAC_STR;*/
static MAC_S stDefBaseMac = DEF_BASEMAC;
static char gszStrMac[64];
rwlock_t HashLock = RW_LOCK_UNLOCKED;
int debug = 0;
/*functions defined in this file*/
static int __init VMETH_Probe(struct net_device *dev);
static int VMETH_Open (struct net_device *dev);
static int VMETH_StartXmit(struct sk_buff *skb, struct net_device *dev);
static void VMETH_Timeout (struct net_device *dev);
static int VMETH_Close (struct net_device *dev);
static struct net_device_stats *VMETH_GetStats(struct net_device *dev);
int VMETH_SetMacAddress(struct net_device *dev,void *addr);
char *MacToStr(const unsigned char *mac, char *szStr);
/*
get mac address from a integer array;
return -1, if failed;
return 0 if success, and put mac to output parameter 'szMac'
*/
int VMETH_GetMacFromIntArray(const int *pArr, char *pstMac)
{
int i;
for(i=0; i<6; i++)
{
if(pArr[i] > 0xFF || pArr[i] < 0){
return -1;
}
pstMac[i] = (unsigned char)pArr[i];
}
return 0;
}
char *MacToStr(const unsigned char *mac, char *szStr)
{
if(mac){
sprintf(szStr,"%02x:%02x:%02x:%02x:%02x:%02x",
(unsigned char)mac[0],
(unsigned char)mac[1],
(unsigned char)mac[2],
(unsigned char)mac[3],
(unsigned char)mac[4],
(unsigned char)mac[5]);
}else{
strcpy(szStr,"00:00:00:00:00:00");
}
return szStr;
}
static int __init VMETH_Probe(struct net_device *dev)
{
SET_MODULE_OWNER(dev);
/*setup phy parameter*/
dev->base_addr = VMETH_IO;
dev->irq = VMETH_IRQ;
dev->open = &VMETH_Open;
dev->hard_start_xmit = &VMETH_StartXmit;
dev->tx_timeout = &VMETH_Timeout;
dev->watchdog_timeo = HZ;
dev->stop = &VMETH_Close;
dev->get_stats = &VMETH_GetStats;
dev->do_ioctl = NULL;
/*
* Setup the generic properties
*/
ether_setup(dev);
/*replace ether_setup's setting*/
dev->set_mac_address = &VMETH_SetMacAddress;
printk("The %d vmeth is setup success, name=%s\n",((struct net_local *)dev->priv)->usIndex,dev->name);
return 0;
}
static int VMETH_Open (struct net_device *dev)
{
netif_start_queue(dev);
printk("The %s vmeth is open success\n",dev->name);
return 0;
}
static int VMETH_StartXmit(struct sk_buff *skb, struct net_device *dev)
{
struct net_local *pstNetLocal = (struct net_local *)dev->priv;
skb->dev = gpstHostDev;
netif_stop_queue(dev);
if(dev_queue_xmit(skb)){
pstNetLocal->stats.tx_errors++;
}else{
pstNetLocal->stats.tx_packets++;
pstNetLocal->stats.tx_bytes += skb->len;
}
netif_start_queue(dev);
return 0;
}
static void VMETH_Timeout (struct net_device *dev)
{
return ;
}
static int VMETH_Close (struct net_device *dev)
{
netif_stop_queue(dev);
printk("The %s vmeth is close success\n",dev->name);
return 0;
}
static struct net_device_stats *VMETH_GetStats(struct net_device *dev)
{
struct net_local *pstNetLocal = (struct net_local *)dev->priv;
return &(pstNetLocal->stats);
}
int VMETH_SetMacAddress(struct net_device *dev,void *p)
{
struct sockaddr *addr=p;
struct net_local *pstNetLocal = (struct net_local *)dev->priv;
if (netif_running(dev)){
return -EBUSY;
}
if(VMETH_DelItemFromHashTable(dev->dev_addr) != 0){
return -1;
}
if(VMETH_InsertItemToHashTable(pstNetLocal->usIndex, addr->sa_data) != 0){
return -1;
}
/*
if(VMETH_HashTableReplaceItem(dev->dev_addr,addr->sa_data,pstNetLocal->usIndex) ){
printk("Hash Table error,not found old mac index\n");
return -1;
}*/
memcpy(dev->dev_addr, addr->sa_data,dev->addr_len);
return 0;
}
int VMETH_Rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
struct net_device *pstMyDev = NULL;
struct ethhdr *pstEthHeader = NULL;
struct net_local *pstNetLocal = NULL;
unsigned short usIndex = VMETH_INVALID_INDEX;
/*is our sended packet, or not from bound device*/
if((dev >= &gstDevTable[0] && dev <= &gstDevTable[VMETH_MAX_VM-1]) || (dev != gpstHostDev)){
#ifdef VMETH_DEBUG
if(debug > 0){
printk("vmeth droped a packet because it is myselft or bound dev's \n");
}
#endif
goto droped;
}
if(skb->pkt_type == PACKET_HOST){
#ifdef VMETH_DEBUG
if(debug > 0){
printk("vmeth droped a packet because pkt_type is PACKET_HOST\n");
}
#endif
goto droped;
}
if((skb = skb_share_check(skb,GFP_ATOMIC)) == NULL){
goto droped;
}
pstEthHeader = skb->mac.ethernet;
if(skb->pkt_type == PACKET_OTHERHOST){
usIndex = VMETH_GetIndexFromHashTable(pstEthHeader->h_dest);
/*search dev failed*/
if(usIndex >= VMETH_HASHTABLE_LEN){
printk("vmeth droped a packet because not found device\n");
goto droped; /*not support promisc mode*/
}
pstMyDev = &gstDevTable[usIndex];
/*device not opened*/
if(netif_queue_stopped(pstMyDev)){
printk("vmeth droped a packet because device not opened\n");
goto droped;
}
pstNetLocal = (struct net_local *)pstMyDev->priv;
skb->pkt_type = PACKET_HOST;
#ifdef VMETH_DEBUG
if(debug > 0){
printk("vmeth receive packet index=%d, dev=%s,pkt_dest_mac=%s\n",usIndex,
pstMyDev->name,MacToStr(pstEthHeader->h_dest,gszStrMac));
}
#endif
skb->dev = pstMyDev;
netif_rx(skb);/*send packet to upper link*/
pstMyDev->last_rx = jiffies;
pstNetLocal->stats.rx_packets +=1;
pstNetLocal->stats.rx_bytes += skb->len;
return 0;
}
{
/*multicast or promisc*/
unsigned short i = 0;
struct sk_buff *nskb = NULL;
for(i = 0; i< VMETH_MAX_VM;i++){
pstMyDev = &gstDevTable[i];
/*device is open, and in promisc/multicast state,or packet is a broacast pakcet*/
if((netif_queue_stopped(pstMyDev) == 0) &&
((pstMyDev->flags & IFF_ALLMULTI) ||
MAC_CMP(pstEthHeader->h_dest,pstMyDev->broadcast) ==0)){
if( (nskb = skb_clone(skb,GFP_ATOMIC)) == NULL){
printk("vmeth out mem\n");
continue;
}
pstNetLocal = (struct net_local *)pstMyDev->priv;
nskb->dev = pstMyDev;
netif_rx(nskb); /*send packet to upper link*/
pstMyDev->last_rx = jiffies;
pstNetLocal->stats.rx_packets +=1;
pstNetLocal->stats.rx_bytes += nskb->len;
}
}
}
kfree_skb(skb);
return 0;
droped:
kfree_skb(skb);
return NET_RX_DROP;
}
#ifdef MODULE
MODULE_AUTHOR("Chen Guoqiang, douspring@263.net");
MODULE_DESCRIPTION("A simulated mutiple ethernet device driver based on a real ethernet device,version 1.0 2003/01/06");
MODULE_LICENSE("GPL");
MODULE_PARM(card_num, "i");
MODULE_PARM(host_dev, "s");
MODULE_PARM(base_mac, "6i");
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(card_num, "How much virtual device");
MODULE_PARM_DESC(host_dev, "Virtual devices is based on which device");
MODULE_PARM_DESC(base_mac, "Virtual devices is based on which mac address");
MODULE_PARM_DESC(debug, "debug switch, 0:close, >0 open");
/**
* init_module:
*
* When the driver is loaded as a module this function is called. We fake up
* a device structure with the base I/O and interrupt set as if it was being
* called from Space.c. This minimises the extra code that would otherwise
* be required.
*
* Returns 0 for success or -EIO if a card is not found. Returning an error
* here also causes the module to be unloaded
*/
static struct packet_type vmeth_packet_type = {
type: __constant_htons(ETH_P_ALL),
func: VMETH_Rcv,
data: (void*) 1, /* understand shared skbs */
};
static int VMETH_Setup()
{
unsigned short i = 0;
struct net_device *pstDev = NULL;
struct net_local *pstNetLocal = NULL;
MAC_S Mac,BaseMac;
if(card_num >= VMETH_MAX_VM){
card_num = VMETH_MAX_VM;
printk("Too large vmeths, cut to %d\n",card_num);
}
if(card_num <=0){
card_num = 1;
printk("Too small vmeths, cut to %d\n",card_num);
}
if(card_num >= 100){
printk("By default,linux kernel do not support eth device more than 100, you should modify kernel\n");
printk("Your setting may be dump the kernel\n");
}
/*Setup mac address hash table*/
VMETH_InitHashTable();
if(VMETH_GetMacFromIntArray(base_mac,BaseMac) != 0){
printk("Invalid base mac, use default\n");
MAC_CPY(BaseMac,stDefBaseMac);
}
MAC_CPY(Mac,BaseMac);
for(i = 0; i< VMETH_MAX_VM;i++){
netif_stop_queue(&gstDevTable[i]);
gstDevTable[i].priv = NULL;
}
for(i = 0; i < card_num; i++){
if(i >= 0xFF){
Mac[4] = (unsigned char)((i+1)>>8);
}
Mac[5] =(unsigned char)((i+1) & 0xFF);
printk("vmeth %d mac %s\n",i,MacToStr(Mac,gszStrMac));
if(VMETH_InsertItemToHashTable(i,Mac) != 0){
printk("Init %d vmeth failed ,maybe mallo failed\n",i);
MAC_CPY(pstDev->dev_addr,Mac);
netif_stop_queue(pstDev);
continue; /*do not register*/
}
pstDev = &gstDevTable[i];
if((pstDev->priv = kmalloc(sizeof(struct net_local),GFP_KERNEL)) == NULL){
printk("vmeth out of mem, index =%d\n",i);
continue;
}
memset(pstDev->priv,0,sizeof(struct net_local));
pstNetLocal = (struct net_local *)pstDev->priv;
pstNetLocal->usIndex = i;
MAC_CPY(pstDev->dev_addr,Mac);
pstDev->init = &VMETH_Probe;
if (register_netdev(pstDev) != 0){
printk("Init %d vmeth failed ,register failed\n",i);
pstDev->state = __LINK_STATE_NOCARRIER;
}
}
return 0;
}
static int __init VMETH_InitModule(void)
{
if((gpstHostDev = dev_get_by_name(host_dev)) == NULL){
return -1;
}
VMETH_Setup();
/*avoid boud dev to be deleted*/
dev_hold(gpstHostDev);
vmeth_packet_type.dev = gpstHostDev;
dev_add_pack(&vmeth_packet_type);
return 0;
}
/**
* cleanup_module:
*
* The module is being unloaded. We unhook our network device from the system
* and then free up the resources we took when the card was found.
*/
static void __exit VMETH_CleanupModule(void)
{
int i;
dev_remove_pack(&vmeth_packet_type);
for(i=0;i<VMETH_MAX_VM;i++)
{
if(gstDevTable[i].priv != NULL){
unregister_netdev(&gstDevTable[i]);
kfree(gstDevTable[i].priv);
gstDevTable[i].priv = NULL;
}
}
dev_put(gpstHostDev);
VMETH_DestroyHashTable();
return;
}
module_init(VMETH_InitModule);
module_exit(VMETH_CleanupModule);
#endif /* MODULE */
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -m486 -c -o 3c501.o 3c501.c"
* kept-new-versions: 5
* End:
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -