linux平台实现虚拟网卡,在系统中模拟出一块虚拟的网卡出来,把发送到这块网卡的数据通过某种途径转发到应用层程序,
由应用层程序负责处理这些数据。
linux内核中有 TUN/TAP,其实已经实现了这个功能,只要 open(“/dev/net/tun",O_RDWR); (2.6内核),然后ioctl创建一个虚拟网卡,
系统中就会出现 名字叫 TUN或者TAP的网卡,接着就可以调用 ifconfig配置网卡地址,
最后就可以调用read/wwrite即可从应用程序读取和写入这块网卡数据。
我们显然可以十分简单的利用现成的TUN/TAP,开发出VPN虚拟私人局域网,但是作为一项技术,能自由灵活的使用和掌握虚拟网卡技术,
自己开发虚拟网卡驱动也是必要的。
下面是虚拟网卡驱动代码 , 分为 vnetwk.h vnetwk.c trans.c三个文件,编译生成 vwk.ko.
很显然他比windows驱动简单多。
// vnetwk.h 头文件,
///By Fanxiushu 2013-01-11
#pragma once
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/if.h>
#include <linux/poll.h>
#define VNETWK_COUNT 10
#define MAX_SKB_QUENE_LEN 100
struct vnetwk_t
{
///
struct cdev chr_dev;
struct class* vwk_cls;
int index;
struct net_device* net_dev;
struct net_device_stats stats;
struct sk_buff_head skb_q;
wait_queue_head_t wait_q;
};
struct netdev_priv_t
{
struct vnetwk_t* vwk;
};
///
int vwk_chr_open( struct inode* ino, struct file* fp);
int vwk_chr_release( struct inode* ino, struct file* fp);
int vwk_chr_read( struct file* fp, char* buf, size_t length, loff_t* offset );
int vwk_chr_write( struct file* fp, const char* buf, size_t length, loff_t* offset );
int vwk_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
unsigned int vwk_chr_poll(struct file *file, poll_table *wait);
/// network
int vwk_net_open(struct net_device *dev);
int vwk_net_stop(struct net_device *dev);
int vwk_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
struct net_device_stats *vwk_net_stats(struct net_device *dev);
void vwk_net_tx_timeout(struct net_device *dev);
int vwk_net_set_mac_address(struct net_device *dev, void *addr);
int vwk_net_change_mtu(struct net_device *dev, int mtu);
int vwk_net_start_xmit(struct sk_buff *skb, struct net_device *dev);
/vnetwk.c
/
/// By Fanxiushu 2013-01-11
#include "vnetwk.h"
struct vnetwk_t* vwk_array[ VNETWK_COUNT ];
static int vwk_count = 1;
static int vwk_devid = 0;
static struct file_operations vwk_fops = {
.open = vwk_chr_open,
.release = vwk_chr_release,
.read = vwk_chr_read,
.write = vwk_chr_write,
.ioctl = vwk_chr_ioctl,
.poll = vwk_chr_poll,
};
static int vwk_netdev_init( struct net_device* dev )
{
struct netdev_priv_t* priv;
priv = (struct netdev_priv_t*)netdev_priv(dev );
/// set func
dev->open = vwk_net_open;
dev->stop = vwk_net_stop;
dev->do_ioctl = vwk_net_ioctl;
dev->get_stats = vwk_net_stats;
dev->tx_timeout = vwk_net_tx_timeout;
dev->set_mac_address = vwk_net_set_mac_address;
dev->change_mtu = vwk_net_change_mtu;
dev->hard_start_xmit = vwk_net_start_xmit;
///
ether_setup( dev );
random_ether_addr(dev->dev_addr);
///
printk( KERN_ALERT" init netdev [index=%d] OK.\n", priv->vwk->index );
return 0;
}
static int vwk_netdev_create( struct vnetwk_t* vwk)
{
char name[15]; int result;
struct netdev_priv_t* priv;
sprintf( name, "vnetwk%d", vwk->index );
vwk->net_dev = alloc_netdev( sizeof( struct netdev_priv_t), name , ether_setup );
if( !vwk->net_dev ) {
printk(KERN_ALERT" alloc_netdev err, index=%d\n", vwk->index );
return -1;
}
priv = (struct netdev_priv_t*)netdev_priv( vwk->net_dev );
priv->vwk = vwk;
vwk->net_dev->init = vwk_netdev_init;
result = register_netdev( vwk->net_dev );
if( result < 0){
printk(KERN_ALERT" register_netdev err <%d> index=%d\n", result, vwk->index);
free_netdev( vwk->net_dev );
vwk->net_dev = NULL;
return -1;
}
///
return 0;
}
static int vwk_netdev_close( struct vnetwk_t* vwk)
{
if( !vwk || !vwk->net_dev ) return -1;
清空SKB队列
skb_queue_purge( &vwk->skb_q );
unregister_netdev( vwk->net_dev );
free_netdev( vwk->net_dev );
vwk->net_dev = NULL;
return 0;
}
static int vwk_device_create_index( int index )
{
int result;
dev_t devt;
struct vnetwk_t* vwk = NULL;
struct device* vwk_dev= NULL;
char tmp_str[20];
devt = MKDEV( vwk_devid, index ); //
vwk = (struct vnetwk_t*)kzalloc( sizeof( struct vnetwk_t), GFP_KERNEL );
if( !vwk ){
printk(KERN_ALERT"kzalloc error.\n");
return -1;
}
vwk->index = index;
skb_queue_head_init( &vwk->skb_q );
init_waitqueue_head( &vwk->wait_q );
/创建网路设备
if( vwk_netdev_create( vwk ) < 0 ) {
printk(KERN_ALERT"Not Create netdevice.\n");
kfree( vwk );
return -1;
}
///注册字符设备
cdev_init( &vwk->chr_dev, &vwk_fops );
vwk->chr_dev.owner = THIS_MODULE;
vwk->chr_dev.ops = &vwk_fops;
result = cdev_add( &vwk->chr_dev, devt, 1 );
if( result ) {
printk(KERN_NOTICE"cdev_add err <%d>, index=%d\n", result, index );
}
///创建设备节点,为了用户程序能访问
sprintf( tmp_str, "cls_vnetwk%d", index );
vwk->vwk_cls = class_create( THIS_MODULE, tmp_str );
if( !vwk->vwk_cls ){
printk(KERN_ALERT"class_create err, index=%d\n", index );
goto E;
}
vwk_dev = device_create( vwk->vwk_cls, NULL, devt, "vnetwk%d", index );
if( !vwk_dev ){
printk( KERN_ALERT"device_create err, index=%d\n", index);
}
vwk_array[ index ] = vwk;
return 0;
E:
vwk_netdev_close( vwk );
cdev_del( &vwk->chr_dev );
if( vwk->vwk_cls ){
device_destroy( vwk->vwk_cls ,devt );
class_destroy( vwk->vwk_cls );
}
kfree(vwk );
return -1;
}
///
static int vwk_device_destroy_index( int index )
{
struct vnetwk_t* vwk = vwk_array[ index ];
if( !vwk ) return -1;
///
vwk_netdev_close( vwk );
cdev_del( &vwk->chr_dev );
if( vwk->vwk_cls ){
device_destroy( vwk->vwk_cls , MKDEV( vwk_devid, index ) );
class_destroy( vwk->vwk_cls );
}
///
kfree( vwk );
vwk_array[index] = NULL;
return 0;
}
static void vwk_device_destroy(void)
{
int i;
for( i=0;i<VNETWK_COUNT;++i){
vwk_device_destroy_index( i );
}
}
static int vnetwk_init(void)
{
int i; dev_t devt;
int result;
for(i=0;i<VNETWK_COUNT;++i){
vwk_array[i] = NULL;
}
if( vwk_count < 1 ) vwk_count = 1;
if( vwk_count > VNETWK_COUNT ) vwk_count = VNETWK_COUNT;
///申请一批设备号,为创建字符设备做准备
devt = MKDEV( vwk_devid, 0 );
if( vwk_devid ){
result = register_chrdev_region( devt, vwk_count, "vnetwk" );
}
else{
result = alloc_chrdev_region( &devt, 0, vwk_count, "vnetwk" );
vwk_devid = MAJOR( devt );
}
if( result < 0 ){
printk(KERN_ALERT"register Device ID Error.\n");
return -1;
}
for(i=0;i< vwk_count;++i){
if( vwk_device_create_index( i ) < 0 ){
vwk_device_destroy();
unregister_chrdev_region( MKDEV( vwk_devid, 0 ), vwk_count );
///
printk(KERN_ALERT"can not create %d device.\n", i );
return -1;
}
}
return 0;
}
static void vnetwk_exit(void)
{
vwk_device_destroy();
unregister_chrdev_region( MKDEV( vwk_devid, 0), vwk_count );
printk(KERN_INFO"vnetwk_exit. \n ");
}
module_param( vwk_count, int, S_IRUGO );
module_param( vwk_devid, int, S_IRUGO );
module_init( vnetwk_init);
module_exit( vnetwk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fanxiushu");
MODULE_DESCRIPTION("A Virtual Network Card Driver");
trans.c
//
//By Fanxiushu 2013-01-11
#include "vnetwk.h"
///字符设备读写
int vwk_chr_open( struct inode* ino, struct file* fp)
{
struct vnetwk_t* vwk = NULL;
vwk = container_of( ino->i_cdev, struct vnetwk_t, chr_dev );
fp->private_data = vwk ;
printk(KERN_NOTICE"vwk_chr_open: index=%d\n", vwk->index );
return 0;
}
int vwk_chr_release( struct inode* ino, struct file* fp)
{
struct vnetwk_t* vwk = NULL;
vwk = container_of( ino->i_cdev, struct vnetwk_t, chr_dev );
return 0;
}
/// read & write
int vwk_chr_read( struct file* fp, char* buf, size_t length, loff_t* offset )
{
struct vnetwk_t* vwk = (struct vnetwk_t*)fp->private_data;
struct sk_buff* skb;
int ret = -1; int rr;
DECLARE_WAITQUEUE(wait, current ); //申明一个等待队列
add_wait_queue( &vwk->wait_q, &wait ); //加入到等待队列
while( 1 ){
set_current_state( TASK_INTERRUPTIBLE ); //设置进程可休眠状态
if( (skb = skb_dequeue( &vwk->skb_q )) ){ //有数据可读
ret = skb->len > length?length:skb->len;
rr = copy_to_user( buf, skb->data, ret ); //从内核空间复制到用户空间
vwk->stats.tx_packets++;
vwk->stats.tx_bytes += ret;
kfree_skb( skb );
netif_wake_queue( vwk->net_dev ); //让上层协议可以继续发送数据包
break;
}
///
if( fp->f_flags & O_NONBLOCK ){ //非阻塞状态
ret = -EAGAIN;
break;
}
if( signal_pending(current )) { //进程被信号中断
ret = -ERESTARTSYS;
break;
}
/其他状态,什么都不做,调用schudle休眠
schedule();
}
set_current_state( TASK_RUNNING ); //设置进程可运行
remove_wait_queue( &vwk->wait_q, &wait ); //移除出等待队列
printk(KERN_NOTICE"CHR_Read: [%d]\n", ret );
return ret;
}
int vwk_chr_write( struct file* fp, const char* buf, size_t length, loff_t* offset )
{
struct vnetwk_t* vwk = (struct vnetwk_t*)fp->private_data;
struct sk_buff* skb;
int rr;
if( length < 0) return -EINVAL;
if( length==0) return 0;
skb = dev_alloc_skb( length+4);
if( !skb ){
vwk->stats.rx_errors++;
printk(KERN_ALERT"dev_alloc_skb error.\n");
return -EINVAL;
}
skb->dev = vwk->net_dev;
skb_reserve( skb, 2 ); //保留 2个字节,干嘛的?
rr = copy_from_user( skb_put(skb, length ), buf, length );
skb->protocol = eth_type_trans(skb, skb->dev );
netif_rx( skb );
vwk->net_dev->last_rx = jiffies; // ?
vwk->stats.rx_packets++;
vwk->stats.rx_bytes += length;
return length;
}
int vwk_chr_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg)
{
struct vnetwk_t* vwk = (struct vnetwk_t*)fp->private_data;
printk(KERN_ALERT"vnetwk chr ioctl [index=%d]\n", vwk->index );
return 0;
}
unsigned int vwk_chr_poll(struct file *fp, poll_table *wait)
{
struct vnetwk_t* vwk = (struct vnetwk_t*)fp->private_data;
int mask = POLLOUT|POLLWRNORM; //随时可写
poll_wait( fp, &vwk->wait_q, wait ); //把等待队列加到wait中,函数立即返回
if( skb_queue_len( &vwk->skb_q ) > 0 ) //有数据可读
mask |= POLLIN|POLLRDNORM;
///
return mask;
}
/// network
//打开网络设备
int vwk_net_open(struct net_device *dev)
{
netif_start_queue(dev);
return 0;
}
//关闭网络设备
int vwk_net_stop(struct net_device *dev)
{
netif_stop_queue(dev);
return 0;
}
//IOCTL
int vwk_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct netdev_priv_t *priv = netdev_priv(dev);
printk(KERN_ALERT"vnetwk net ioctl [index=%d] \n",priv->vwk->index );
///
return 0;
}
//获得设备状态
struct net_device_stats *vwk_net_stats(struct net_device *dev)
{
struct netdev_priv_t *priv = netdev_priv(dev);
return &priv->vwk->stats;
}
//超时
void vwk_net_tx_timeout(struct net_device *dev)
{
struct netdev_priv_t *priv = netdev_priv(dev);
printk(KERN_WARNING "%s: Transmission timed out.\n", dev->name);
priv->vwk->stats.tx_errors++; //
///
netif_wake_queue(dev);
}
//设置网卡MAC
int vwk_net_set_mac_address(struct net_device *dev, void *addr)
{
//struct netdev_priv_t *priv = netdev_priv(dev);
struct sockaddr *s = (struct sockaddr *)addr;
///
if( netif_running(dev) != 0 ) {//状态,表示网卡正在运行
printk(KERN_ALERT"vnetwk set_mac_address err; [netif_running]. \n");
return -1;
}
memcpy( dev->dev_addr, s->sa_data, ETH_ALEN );
///
printk(KERN_INFO "%s: Setting MAC address to `%02x:%02x:%02x:%02x:%02x:%02x`.\n", dev->name,
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5] );
return 0;
}
//改变网卡MTU
int vwk_net_change_mtu(struct net_device *dev, int mtu)
{
printk(KERN_ALERT"change_mtu [MTU=%d].\n", mtu);
///
return 0;
}
//从上层发送数据到网卡
int vwk_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct vnetwk_t* vwk;
struct netdev_priv_t *priv = netdev_priv(dev);
vwk = priv->vwk;
//检查是否有太多数据包等待在队列里
if( skb_queue_len( &vwk->skb_q ) >= MAX_SKB_QUENE_LEN ){
vwk->stats.tx_fifo_errors++;
vwk->stats.tx_dropped++;
kfree_skb( skb );
printk(KERN_INFO"hard_start_xmit packet out of MAX_SKB_QUEUE_LEN.\n");
return 0;
}
///
netif_stop_queue( dev );
skb_queue_tail( &vwk->skb_q, skb ); //添加SKB到队列里,这里 skb_queue_tail内部已经是加锁,所以不再另外加锁
netif_wake_queue( dev );
dev->trans_start = jiffies; //记录传输开始时间
//唤醒进入休眠等待获得数据的 vwk_chr_read
wake_up_interruptible( &vwk->wait_q );
return 0;
}
/ Makefile
# By Fanxiushu 2013 01-11
#
obj-m += vwk.o
vwk-objs = vnetwk.o trans.o
KERNEL_DIR=/lib/modules/$(shell uname -r)/build
PWD=$(shell pwd)
all:
make -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules
clean:
-rm *.mod.c *.ko *.o
///
分为 vnetwk.h vnetwk.c trans.c三个文件,编译生成 vwk.ko.
insmod vwk.ko vwk_count=5
会在加载驱动时候生成 vnetwk0-4 共5块网卡,
可以调用 ifconfig动态分配IP地址,或者在 /etc/sysconfig/network-scripts/ifcfg-vnetwk0-4 等脚本文件中,静态分配IP地址。
应用层程序简单接口代码。
struct _net_card_t
{
int fd;
unsigned char mac_address[ 6 ];
};
void* nc_open( const char* dev_name )
{
if( !dev_name) return NULL;
_net_card_t* nc = new _net_card_t;
memset( nc->mac_address,0, 6 );
char name[260] ;
sprintf(name, "/dev/%s", dev_name );
nc->fd = open( name , O_RDWR );
if( nc->fd< 0 ) {
delete nc;
printf("can not open [%s]\n", dev_name );
return NULL;
}
return nc;
}
void nc_close( void* handle )
{
if( !handle ) return ;
_net_card_t* nc = (_net_card_t*)handle;
close( nc->fd );
///
delete nc;
}
int nc_read( void* handle, char* buf, int len, int tmo_sec )
{
_net_card_t* nc = (_net_card_t*)handle;
if(!nc)return -1;
while( true ){
fd_set rdst; FD_ZERO(&rdst); FD_SET(nc->fd,&rdst);
struct timeval timeout; timeout.tv_sec = tmo_sec; timeout.tv_usec = 0;
int status = select( nc->fd + 1,&rdst,NULL,NULL,tmo_sec != 0 ? &timeout : NULL);
if( status < 0 ) return -1;
if( status ==0 ) return 0;
int ret = read( nc->fd, buf, len );
if( ret < 14 ){ //小于以太网或者出错
if( ret<0) return -1;
return 0 ;
}
if( nc_filter_read_data( buf, ret) == 0 ){
return ret;
}
//
}
return 0;
}
int nc_write( void* handle, char* buf, int len, int tmo_sec )
{
_net_card_t* nc = (_net_card_t*)handle;
if( !nc) return -1;
///
return write( nc->fd, buf, len ) ;
}
///
由应用层程序负责处理这些数据。
linux内核中有 TUN/TAP,其实已经实现了这个功能,只要 open(“/dev/net/tun",O_RDWR); (2.6内核),然后ioctl创建一个虚拟网卡,
系统中就会出现 名字叫 TUN或者TAP的网卡,接着就可以调用 ifconfig配置网卡地址,
最后就可以调用read/wwrite即可从应用程序读取和写入这块网卡数据。
我们显然可以十分简单的利用现成的TUN/TAP,开发出VPN虚拟私人局域网,但是作为一项技术,能自由灵活的使用和掌握虚拟网卡技术,
自己开发虚拟网卡驱动也是必要的。
下面是虚拟网卡驱动代码 , 分为 vnetwk.h vnetwk.c trans.c三个文件,编译生成 vwk.ko.
很显然他比windows驱动简单多。
// vnetwk.h 头文件,
///By Fanxiushu 2013-01-11
#pragma once
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/if.h>
#include <linux/poll.h>
#define VNETWK_COUNT 10
#define MAX_SKB_QUENE_LEN 100
struct vnetwk_t
{
///
struct cdev chr_dev;
struct class* vwk_cls;
int index;
struct net_device* net_dev;
struct net_device_stats stats;
struct sk_buff_head skb_q;
wait_queue_head_t wait_q;
};
struct netdev_priv_t
{
struct vnetwk_t* vwk;
};
///
int vwk_chr_open( struct inode* ino, struct file* fp);
int vwk_chr_release( struct inode* ino, struct file* fp);
int vwk_chr_read( struct file* fp, char* buf, size_t length, loff_t* offset );
int vwk_chr_write( struct file* fp, const char* buf, size_t length, loff_t* offset );
int vwk_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
unsigned int vwk_chr_poll(struct file *file, poll_table *wait);
/// network
int vwk_net_open(struct net_device *dev);
int vwk_net_stop(struct net_device *dev);
int vwk_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
struct net_device_stats *vwk_net_stats(struct net_device *dev);
void vwk_net_tx_timeout(struct net_device *dev);
int vwk_net_set_mac_address(struct net_device *dev, void *addr);
int vwk_net_change_mtu(struct net_device *dev, int mtu);
int vwk_net_start_xmit(struct sk_buff *skb, struct net_device *dev);
/vnetwk.c
/
/// By Fanxiushu 2013-01-11
#include "vnetwk.h"
struct vnetwk_t* vwk_array[ VNETWK_COUNT ];
static int vwk_count = 1;
static int vwk_devid = 0;
static struct file_operations vwk_fops = {
.open = vwk_chr_open,
.release = vwk_chr_release,
.read = vwk_chr_read,
.write = vwk_chr_write,
.ioctl = vwk_chr_ioctl,
.poll = vwk_chr_poll,
};
static int vwk_netdev_init( struct net_device* dev )
{
struct netdev_priv_t* priv;
priv = (struct netdev_priv_t*)netdev_priv(dev );
/// set func
dev->open = vwk_net_open;
dev->stop = vwk_net_stop;
dev->do_ioctl = vwk_net_ioctl;
dev->get_stats = vwk_net_stats;
dev->tx_timeout = vwk_net_tx_timeout;
dev->set_mac_address = vwk_net_set_mac_address;
dev->change_mtu = vwk_net_change_mtu;
dev->hard_start_xmit = vwk_net_start_xmit;
///
ether_setup( dev );
random_ether_addr(dev->dev_addr);
///
printk( KERN_ALERT" init netdev [index=%d] OK.\n", priv->vwk->index );
return 0;
}
static int vwk_netdev_create( struct vnetwk_t* vwk)
{
char name[15]; int result;
struct netdev_priv_t* priv;
sprintf( name, "vnetwk%d", vwk->index );
vwk->net_dev = alloc_netdev( sizeof( struct netdev_priv_t), name , ether_setup );
if( !vwk->net_dev ) {
printk(KERN_ALERT" alloc_netdev err, index=%d\n", vwk->index );
return -1;
}
priv = (struct netdev_priv_t*)netdev_priv( vwk->net_dev );
priv->vwk = vwk;
vwk->net_dev->init = vwk_netdev_init;
result = register_netdev( vwk->net_dev );
if( result < 0){
printk(KERN_ALERT" register_netdev err <%d> index=%d\n", result, vwk->index);
free_netdev( vwk->net_dev );
vwk->net_dev = NULL;
return -1;
}
///
return 0;
}
static int vwk_netdev_close( struct vnetwk_t* vwk)
{
if( !vwk || !vwk->net_dev ) return -1;
清空SKB队列
skb_queue_purge( &vwk->skb_q );
unregister_netdev( vwk->net_dev );
free_netdev( vwk->net_dev );
vwk->net_dev = NULL;
return 0;
}
static int vwk_device_create_index( int index )
{
int result;
dev_t devt;
struct vnetwk_t* vwk = NULL;
struct device* vwk_dev= NULL;
char tmp_str[20];
devt = MKDEV( vwk_devid, index ); //
vwk = (struct vnetwk_t*)kzalloc( sizeof( struct vnetwk_t), GFP_KERNEL );
if( !vwk ){
printk(KERN_ALERT"kzalloc error.\n");
return -1;
}
vwk->index = index;
skb_queue_head_init( &vwk->skb_q );
init_waitqueue_head( &vwk->wait_q );
/创建网路设备
if( vwk_netdev_create( vwk ) < 0 ) {
printk(KERN_ALERT"Not Create netdevice.\n");
kfree( vwk );
return -1;
}
///注册字符设备
cdev_init( &vwk->chr_dev, &vwk_fops );
vwk->chr_dev.owner = THIS_MODULE;
vwk->chr_dev.ops = &vwk_fops;
result = cdev_add( &vwk->chr_dev, devt, 1 );
if( result ) {
printk(KERN_NOTICE"cdev_add err <%d>, index=%d\n", result, index );
}
///创建设备节点,为了用户程序能访问
sprintf( tmp_str, "cls_vnetwk%d", index );
vwk->vwk_cls = class_create( THIS_MODULE, tmp_str );
if( !vwk->vwk_cls ){
printk(KERN_ALERT"class_create err, index=%d\n", index );
goto E;
}
vwk_dev = device_create( vwk->vwk_cls, NULL, devt, "vnetwk%d", index );
if( !vwk_dev ){
printk( KERN_ALERT"device_create err, index=%d\n", index);
}
vwk_array[ index ] = vwk;
return 0;
E:
vwk_netdev_close( vwk );
cdev_del( &vwk->chr_dev );
if( vwk->vwk_cls ){
device_destroy( vwk->vwk_cls ,devt );
class_destroy( vwk->vwk_cls );
}
kfree(vwk );
return -1;
}
///
static int vwk_device_destroy_index( int index )
{
struct vnetwk_t* vwk = vwk_array[ index ];
if( !vwk ) return -1;
///
vwk_netdev_close( vwk );
cdev_del( &vwk->chr_dev );
if( vwk->vwk_cls ){
device_destroy( vwk->vwk_cls , MKDEV( vwk_devid, index ) );
class_destroy( vwk->vwk_cls );
}
///
kfree( vwk );
vwk_array[index] = NULL;
return 0;
}
static void vwk_device_destroy(void)
{
int i;
for( i=0;i<VNETWK_COUNT;++i){
vwk_device_destroy_index( i );
}
}
static int vnetwk_init(void)
{
int i; dev_t devt;
int result;
for(i=0;i<VNETWK_COUNT;++i){
vwk_array[i] = NULL;
}
if( vwk_count < 1 ) vwk_count = 1;
if( vwk_count > VNETWK_COUNT ) vwk_count = VNETWK_COUNT;
///申请一批设备号,为创建字符设备做准备
devt = MKDEV( vwk_devid, 0 );
if( vwk_devid ){
result = register_chrdev_region( devt, vwk_count, "vnetwk" );
}
else{
result = alloc_chrdev_region( &devt, 0, vwk_count, "vnetwk" );
vwk_devid = MAJOR( devt );
}
if( result < 0 ){
printk(KERN_ALERT"register Device ID Error.\n");
return -1;
}
for(i=0;i< vwk_count;++i){
if( vwk_device_create_index( i ) < 0 ){
vwk_device_destroy();
unregister_chrdev_region( MKDEV( vwk_devid, 0 ), vwk_count );
///
printk(KERN_ALERT"can not create %d device.\n", i );
return -1;
}
}
return 0;
}
static void vnetwk_exit(void)
{
vwk_device_destroy();
unregister_chrdev_region( MKDEV( vwk_devid, 0), vwk_count );
printk(KERN_INFO"vnetwk_exit. \n ");
}
module_param( vwk_count, int, S_IRUGO );
module_param( vwk_devid, int, S_IRUGO );
module_init( vnetwk_init);
module_exit( vnetwk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fanxiushu");
MODULE_DESCRIPTION("A Virtual Network Card Driver");
trans.c
//
//By Fanxiushu 2013-01-11
#include "vnetwk.h"
///字符设备读写
int vwk_chr_open( struct inode* ino, struct file* fp)
{
struct vnetwk_t* vwk = NULL;
vwk = container_of( ino->i_cdev, struct vnetwk_t, chr_dev );
fp->private_data = vwk ;
printk(KERN_NOTICE"vwk_chr_open: index=%d\n", vwk->index );
return 0;
}
int vwk_chr_release( struct inode* ino, struct file* fp)
{
struct vnetwk_t* vwk = NULL;
vwk = container_of( ino->i_cdev, struct vnetwk_t, chr_dev );
return 0;
}
/// read & write
int vwk_chr_read( struct file* fp, char* buf, size_t length, loff_t* offset )
{
struct vnetwk_t* vwk = (struct vnetwk_t*)fp->private_data;
struct sk_buff* skb;
int ret = -1; int rr;
DECLARE_WAITQUEUE(wait, current ); //申明一个等待队列
add_wait_queue( &vwk->wait_q, &wait ); //加入到等待队列
while( 1 ){
set_current_state( TASK_INTERRUPTIBLE ); //设置进程可休眠状态
if( (skb = skb_dequeue( &vwk->skb_q )) ){ //有数据可读
ret = skb->len > length?length:skb->len;
rr = copy_to_user( buf, skb->data, ret ); //从内核空间复制到用户空间
vwk->stats.tx_packets++;
vwk->stats.tx_bytes += ret;
kfree_skb( skb );
netif_wake_queue( vwk->net_dev ); //让上层协议可以继续发送数据包
break;
}
///
if( fp->f_flags & O_NONBLOCK ){ //非阻塞状态
ret = -EAGAIN;
break;
}
if( signal_pending(current )) { //进程被信号中断
ret = -ERESTARTSYS;
break;
}
/其他状态,什么都不做,调用schudle休眠
schedule();
}
set_current_state( TASK_RUNNING ); //设置进程可运行
remove_wait_queue( &vwk->wait_q, &wait ); //移除出等待队列
printk(KERN_NOTICE"CHR_Read: [%d]\n", ret );
return ret;
}
int vwk_chr_write( struct file* fp, const char* buf, size_t length, loff_t* offset )
{
struct vnetwk_t* vwk = (struct vnetwk_t*)fp->private_data;
struct sk_buff* skb;
int rr;
if( length < 0) return -EINVAL;
if( length==0) return 0;
skb = dev_alloc_skb( length+4);
if( !skb ){
vwk->stats.rx_errors++;
printk(KERN_ALERT"dev_alloc_skb error.\n");
return -EINVAL;
}
skb->dev = vwk->net_dev;
skb_reserve( skb, 2 ); //保留 2个字节,干嘛的?
rr = copy_from_user( skb_put(skb, length ), buf, length );
skb->protocol = eth_type_trans(skb, skb->dev );
netif_rx( skb );
vwk->net_dev->last_rx = jiffies; // ?
vwk->stats.rx_packets++;
vwk->stats.rx_bytes += length;
return length;
}
int vwk_chr_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg)
{
struct vnetwk_t* vwk = (struct vnetwk_t*)fp->private_data;
printk(KERN_ALERT"vnetwk chr ioctl [index=%d]\n", vwk->index );
return 0;
}
unsigned int vwk_chr_poll(struct file *fp, poll_table *wait)
{
struct vnetwk_t* vwk = (struct vnetwk_t*)fp->private_data;
int mask = POLLOUT|POLLWRNORM; //随时可写
poll_wait( fp, &vwk->wait_q, wait ); //把等待队列加到wait中,函数立即返回
if( skb_queue_len( &vwk->skb_q ) > 0 ) //有数据可读
mask |= POLLIN|POLLRDNORM;
///
return mask;
}
/// network
//打开网络设备
int vwk_net_open(struct net_device *dev)
{
netif_start_queue(dev);
return 0;
}
//关闭网络设备
int vwk_net_stop(struct net_device *dev)
{
netif_stop_queue(dev);
return 0;
}
//IOCTL
int vwk_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct netdev_priv_t *priv = netdev_priv(dev);
printk(KERN_ALERT"vnetwk net ioctl [index=%d] \n",priv->vwk->index );
///
return 0;
}
//获得设备状态
struct net_device_stats *vwk_net_stats(struct net_device *dev)
{
struct netdev_priv_t *priv = netdev_priv(dev);
return &priv->vwk->stats;
}
//超时
void vwk_net_tx_timeout(struct net_device *dev)
{
struct netdev_priv_t *priv = netdev_priv(dev);
printk(KERN_WARNING "%s: Transmission timed out.\n", dev->name);
priv->vwk->stats.tx_errors++; //
///
netif_wake_queue(dev);
}
//设置网卡MAC
int vwk_net_set_mac_address(struct net_device *dev, void *addr)
{
//struct netdev_priv_t *priv = netdev_priv(dev);
struct sockaddr *s = (struct sockaddr *)addr;
///
if( netif_running(dev) != 0 ) {//状态,表示网卡正在运行
printk(KERN_ALERT"vnetwk set_mac_address err; [netif_running]. \n");
return -1;
}
memcpy( dev->dev_addr, s->sa_data, ETH_ALEN );
///
printk(KERN_INFO "%s: Setting MAC address to `%02x:%02x:%02x:%02x:%02x:%02x`.\n", dev->name,
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5] );
return 0;
}
//改变网卡MTU
int vwk_net_change_mtu(struct net_device *dev, int mtu)
{
printk(KERN_ALERT"change_mtu [MTU=%d].\n", mtu);
///
return 0;
}
//从上层发送数据到网卡
int vwk_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct vnetwk_t* vwk;
struct netdev_priv_t *priv = netdev_priv(dev);
vwk = priv->vwk;
//检查是否有太多数据包等待在队列里
if( skb_queue_len( &vwk->skb_q ) >= MAX_SKB_QUENE_LEN ){
vwk->stats.tx_fifo_errors++;
vwk->stats.tx_dropped++;
kfree_skb( skb );
printk(KERN_INFO"hard_start_xmit packet out of MAX_SKB_QUEUE_LEN.\n");
return 0;
}
///
netif_stop_queue( dev );
skb_queue_tail( &vwk->skb_q, skb ); //添加SKB到队列里,这里 skb_queue_tail内部已经是加锁,所以不再另外加锁
netif_wake_queue( dev );
dev->trans_start = jiffies; //记录传输开始时间
//唤醒进入休眠等待获得数据的 vwk_chr_read
wake_up_interruptible( &vwk->wait_q );
return 0;
}
/ Makefile
# By Fanxiushu 2013 01-11
#
obj-m += vwk.o
vwk-objs = vnetwk.o trans.o
KERNEL_DIR=/lib/modules/$(shell uname -r)/build
PWD=$(shell pwd)
all:
make -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules
clean:
-rm *.mod.c *.ko *.o
///
分为 vnetwk.h vnetwk.c trans.c三个文件,编译生成 vwk.ko.
insmod vwk.ko vwk_count=5
会在加载驱动时候生成 vnetwk0-4 共5块网卡,
可以调用 ifconfig动态分配IP地址,或者在 /etc/sysconfig/network-scripts/ifcfg-vnetwk0-4 等脚本文件中,静态分配IP地址。
应用层程序简单接口代码。
struct _net_card_t
{
int fd;
unsigned char mac_address[ 6 ];
};
void* nc_open( const char* dev_name )
{
if( !dev_name) return NULL;
_net_card_t* nc = new _net_card_t;
memset( nc->mac_address,0, 6 );
char name[260] ;
sprintf(name, "/dev/%s", dev_name );
nc->fd = open( name , O_RDWR );
if( nc->fd< 0 ) {
delete nc;
printf("can not open [%s]\n", dev_name );
return NULL;
}
return nc;
}
void nc_close( void* handle )
{
if( !handle ) return ;
_net_card_t* nc = (_net_card_t*)handle;
close( nc->fd );
///
delete nc;
}
int nc_read( void* handle, char* buf, int len, int tmo_sec )
{
_net_card_t* nc = (_net_card_t*)handle;
if(!nc)return -1;
while( true ){
fd_set rdst; FD_ZERO(&rdst); FD_SET(nc->fd,&rdst);
struct timeval timeout; timeout.tv_sec = tmo_sec; timeout.tv_usec = 0;
int status = select( nc->fd + 1,&rdst,NULL,NULL,tmo_sec != 0 ? &timeout : NULL);
if( status < 0 ) return -1;
if( status ==0 ) return 0;
int ret = read( nc->fd, buf, len );
if( ret < 14 ){ //小于以太网或者出错
if( ret<0) return -1;
return 0 ;
}
if( nc_filter_read_data( buf, ret) == 0 ){
return ret;
}
//
}
return 0;
}
int nc_write( void* handle, char* buf, int len, int tmo_sec )
{
_net_card_t* nc = (_net_card_t*)handle;
if( !nc) return -1;
///
return write( nc->fd, buf, len ) ;
}
///