linux虚拟网卡驱动代码

49 篇文章 11 订阅
17 篇文章 1 订阅
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 ) ;
}
///
  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值