精华内容
下载资源
问答
  • 基于Linux虚拟网卡测试平台的系统设计.pdf
  • linux虚拟网卡驱动

    千次阅读 2017-11-13 13:14:07
    之前的前两篇写w5500网卡驱动是我的思路有偏差,用的是一种取巧的方法,在linux的用户空间利用spidev直接进行w5500的设置与tcp连接,这只能叫做是一个w5500的应用程序驱动,虽然能达到相应的目的,但是感觉是一种...

            之前的前两篇写w5500网卡驱动是我的思路有偏差,用的是一种取巧的方法,在linux的用户空间利用spidev直接进行w5500的设置与tcp连接,这只能叫做是一个w5500的应用程序驱动,虽然能达到相应的目的,但是感觉是一种取巧的方法,没能充分利用到linux内核强大的任务多线程能力,所以决定编写调试一个w5500的内核网络设备驱动,自己也借此学习linux的网络设备驱动等相关知识。
           现在硬件也有所升级,mcu由stm32f407升级为stm32f429,内部内存存储升为2M,外部又加了2M的sram,这样内存是完全能够满足的,stm32f407和stm32f429是完全兼容的,把之前的f407Uclinux内核做相对应的修改即可在stm32f429运行
           由于对linux网络设备驱动的知识缺乏了解就去查阅了相应的资料,看了书和韦东山老师的视频,有了一点认识,于是先做一个简单的虚拟网卡驱动来试验一下。
    步骤:
    1. 分配一个net_device结构体
    2. 设置:
    2.1 发包函数: hard_start_xmit //高版本linux有所修改
    2.2 收到数据时(在中断处理函数里)用netif_rx上报数据
    2.3 其他设置
    3. 注册: register_netdevice


    这里写图片描述

           具体思路大致是这样并没有错误,但是随着linux内核版本的升级,网络设备驱动的相关函数和数据结构都发生了不少变化,我用的是2.6.33这个版本的linux内核,直接编译韦老师的驱动代码会冒出很多编译错误,这里面的发包函数hard_start_xmit我已经找不到了,net_device_ops结构体的新加入,它里面的.ndo_start_xmit函数,就是发包函数,所以要对net_device_ops结构体进行填充。


    这里写图片描述
           于是参照韦老师视频的虚拟网卡设备驱动程序进行相应的修改,几经调试终于实现了。

    参考的韦老师的程序,具体程序在这里下载http://download.csdn.net/download/jccg89104/10116263

    在linux内核设置中选择相应选项


    这里写图片描述

    编译内核,把内核映像拷入单片机,然后进行相应操作
    测试:
    1. ifconfig // 查看,无网络设备
    2. ifconfig vnet0 3.3.3.3
    ifconfig // 查看,出现vnet0,可见虚拟网卡的ip已经设置为3.3.3.3
    3.
    ping 3.3.3.4 // 试ping3.3.3.4成功,并且有回应,因为在程序中已做相关修改回应处理


    这里写图片描述

           经过这个虚拟网卡设备驱动的编写调试,对linux网络设备驱动有了一个清楚的理解,接下来就要去编写和调试w5500网卡设备驱动了!!

    展开全文
  • 嵌入式Linux虚拟网卡驱动中数据包的提取及转发技术.pdf
  • linux虚拟网卡驱动代码

    千次阅读 热门讨论 2013-01-21 17:35:38
    linux平台实现虚拟网卡,在系统中模拟出一块虚拟的网卡出来,把发送到这块网卡的数据通过某种途径转发到应用层程序, 由应用层程序负责处理这些数据。  linux内核中有 TUN/TAP,其实已经实现了这个功能,只要 ...
    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虚拟网卡技术构建数传电台TCP_IP通信平台的研究.pdf
  • 一种基于VM技术的Linux虚拟网卡桥接方案.pdf
  • [Linux驱动开发] 网络设备之虚拟网卡

    千次阅读 2012-03-04 17:50:54
    以下是一个简单的虚拟网卡驱动,目的是使初学者对网卡驱动的整体架构有一个初步的了解 代码编译加载(insmod virtnet.ko)后,会形成一个自己的虚拟网卡(ifconfig -a 可查看全部网卡信息), MAC address = 00:...
     以下是一个简单的虚拟网卡驱动,目的是使初学者对网卡驱动的整体架构有一个初步的了解 
    

    代码编译加载(insmod virtnet.ko)后,会形成一个自己的虚拟网卡(ifconfig -a 可查看全部网卡信息)

    MAC address = 00:12:34:56:78:9a,可以通过ifconfig "ether" 192.168.1.1 up 对虚拟网卡设置IP地址(ifconfig "ether" 192.168.1.1 down 卸载)

    测试:

    ping 192.168.1.1

    ping 192.168.1.2 即会不停地向外发送数据,此时数据包其实是没有经过网卡设备层的

    但是,我自己测试一直没有加载上,待后续弄清楚后再来更新!!!奋斗

    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/module.h>
    #include <linux/ioport.h>
    #include <linux/netdevice.h>
    #include <linux/etherdevice.h>
    #include <linux/skbuff.h>
    #include <linux/version.h>
    #include <asm/dma.h>
    #include <linux/spinlock.h>
    #include <linux/crc32.h>
    #include <linux/mii.h>
    #include <linux/ethtool.h>
    #include <asm/uaccess.h>

     

    //声明一个网络设备结构体

    struct net_device *dev = NULL;

    //启动数据包的传输,传递参数skb,使得驱动程序能获取从上层传递下来的数据包

    static int virtnet_start_xmit(struct sk_buf *skb, struct net_device *dev)

    {

            printf("send %d bytes\n", skb->len);

            // 更改统计数据

            dev->stats.tx_packets++;

            dev->stats.tx_bytes += skb->len;

            return 0;

    }

    //入口函数

    static int __init virtnet_init(void)

    {

            1、分配一个维护网卡的结构

            dev = alloc_netdev(0, "virtnet%d", ether_setup);

            2、配置网卡特性

            dev->hard_start_xmit = &virtnet_start_xmit;

            /*MAC address*/

            dev->dev_addr[0] = 0x00;

            dev->dev_addr[0] = 0x12;

            dev->dev_addr[0] = 0x34;

            dev->dev_addr[0] = 0x56;

            dev->dev_addr[0] = 0x78;

            dev->dev_addr[0] = 0x9a;

            3、注册网卡设备

            register_netdev(dev);

            return 0;

    }

     

    //出口函数

    static void __exit virtnet_exit(void)

    {

            unregister_netdev(dev);

            free_netdev(dev);

            return;

    }

     

    //注册

    module_init(virtnet_init);

    module_exit(virtnet_exit);

    MODULE_LICENSE("GPL");

    展开全文
  • Linux驱动之虚拟网卡

    千次阅读 2015-10-07 21:40:50
    反正我ping其他ip地址都是能够ping通的,就是不能够ping通自己...虚拟网卡驱动总结如下: /*  * 参考 drivers\net\cs89x0.c  */ static struct net_device *vnet_dev; static void emulator_rx_packet(struct sk_bu

    写网卡驱动之前我总结一下个人的一些观点:其实写驱动并不是大家想想的那么难,这里我客观评价一下内核层和应用层的区别:

    底层:

    工作在内核层的朋友应该有这种感觉,才开始学的时候真的很难,也就是说上手难,我就拿Linux驱动来说吧,写一个完整的驱动,你得装一个虚拟机跑Linux吧,用来编译驱动程序,虚拟机里面需要安装一些库和工具,驱动程序必须跑在一个完整的系统上,所以首先你得搭建好整个系统,你还得了解硬件时序等,这些东西对新手来说真的是够头痛了,但是你会发现你真正的成为一个驱动开发人员后你就有一种豁然开朗的感觉,原来写驱动程序这么简单,框架是不变的。所以说工作在底层的软件开发朋友们也不要觉得自己是多厉害,只是起点稍微高了一点。

    应用层:

    应用层相对来说上手就简单一点了,主要有以下几个方面的体现:

    第一、用到的工具很少,写应用程序基本都是集成开发环境下,就那么一个工具,编译成功基本就可以使用。

    第二、调试代码的时候是最方便的,加一些打印语句,然后运行就能够找到逻辑结构的错误。非常节省时间

    第三、出现错误了,网上一贴,基本上问题就解决了。

    难点:写一个应用程序逻辑结构思维是非常强的,你的代码量也相对的多每天接触的东西有可能不一样,需要不断的去更新自己的知识。

    以上只是个人的观点,如有不同见解的可以留言。


    写这个代码时遇到了一个问题,至今没有得到解决,问题叙述如下:我ping其他ip地址都是能够ping通的,就是不能够ping通自己,我目前也不知道原因,有知道原因的朋友,希望能够分享一下。



    虚拟网卡驱动总结如下:

    /*
     * 参考 drivers\net\cs89x0.c
     */
    static struct net_device *vnet_dev;


    static void emulator_rx_packet(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 virt_net_send_packet(struct sk_buff *skb, struct net_device *dev)
    {
    static int cnt = 0;
    printk("virt_net_send_packet cnt = %d\n", ++cnt);


    /* 对于真实的网卡, 把skb里的数据通过网卡发送出去 */
    netif_stop_queue(dev); /* 停止该网卡的队列 */
        /* ...... */           /* 把skb的数据写入网卡 */


    /* 构造一个假的sk_buff,上报 */
    emulator_rx_packet(skb, dev);

    dev_kfree_skb (skb);   /* 释放skb */
    netif_wake_queue(dev); /* 数据全部发送出去后,唤醒网卡的队列 */

    /* 更新统计信息 */
    dev->stats.tx_packets++;
    dev->stats.tx_bytes += skb->len;

    return 0;
    }

    static int virt_net_init(void)
    {
    /* 1. 分配一个net_device结构体 */
    vnet_dev = alloc_netdev(0, "vnet%d", ether_setup);;  /* alloc_etherdev */

    /* 2. 设置 */
    vnet_dev->hard_start_xmit = virt_net_send_packet;

    /* 设置MAC地址 */
        vnet_dev->dev_addr[0] = 0x08;
        vnet_dev->dev_addr[1] = 0x89;
        vnet_dev->dev_addr[2] = 0x89;
        vnet_dev->dev_addr[3] = 0x89;
        vnet_dev->dev_addr[4] = 0x89;
        vnet_dev->dev_addr[5] = 0x11;


       /* 设置下面两项才能ping通 */
    vnet_dev->flags           |= IFF_NOARP;
    vnet_dev->features        |= NETIF_F_NO_CSUM;


    /* 3. 注册 */
    //register_netdevice(vnet_dev);
    register_netdev(vnet_dev);

    return 0;
    }


    static void virt_net_exit(void)
    {
    unregister_netdev(vnet_dev);
    free_netdev(vnet_dev);
    }



    展开全文
  • windows虚拟网卡驱动开发

    千次阅读 热门讨论 2017-04-06 19:05:24
    by fanxiushu 2017-04-06 转载或...很早前的文章介绍过windows和linux平台的虚拟网卡技术, 详见 http://blog.csdn.net/fanxiushu/article/details/8526708 http://blog.csdn.net/fanxiushu/article/details/8526
  • Linux虚拟网卡测试平台的实现.pdf
  • 十九、Linux驱动之虚拟网卡驱动

    千次阅读 2018-12-11 17:22:46
    1. 基本概念  网络设备是完成用户数据包在网络媒介上发送和接收的设备,它将上层协议传递下来的数据包以特定的媒介访问控制方式进行发送,并将接收到的数据包... Linux系统对网络设备驱动定义了4个层次, 从上到...
  • 我们来实现这么一个目的 我ping 3.3.3.4的时候,按理说如果是真实网卡的话,我们3.3.3.3的机器,和3.3.3.4的机器。ping 3.3.3.4的时候3.3.3.3的机器会把包发给...但是我们是虚拟网卡,并没有真正的网线,也没有3.3.3
  • 本课程是linux驱动开发的第11个课程,主要内容是linux的网络驱动的介绍,首先讲述了网络设备驱动接口和之前讲的2种的不同,然后以一个虚拟网卡驱动源码学习了网卡驱动的框架,后分析了一个实际网卡DM9000的驱动细节...
  • 前一段时间,一直在找寻windows操作系统上的虚拟网卡接口,主要是为了搭建隧道使用。但是windows操作系统不像Linux操作系统,它的代码不开源,导致这方面的资料很少,因此花费了较长时间来寻找相关实现框架,最终...
  • linux下实现虚拟网卡TAP/TUN例子

    千次阅读 2018-04-10 16:45:20
    原文地址::https://blog.csdn.net/hshl1214/article/details/52936065相关文章1、linux驱动程序之虚拟以太网设备vmeth----https://blog.csdn.net/hnhbdss/article/details/15376052、Linux下Tun/Tap设备通信原理----...
  • 实现虚拟网卡代码

    2014-10-08 23:58:53
    linux下实现虚拟网卡技术,该代码使用c语言实现,已经测试可以使用。
  • 1、回环网卡:它是一种虚拟设备,原理是自发自收,形成逻辑上的回路       2、回环网卡驱动设计步骤   设备初始化: 1、分配网卡设备:struct net_device *dev = alloc_netdev(0, "lo", loopback_setup);...
  • linux pci网卡驱动

    2009-11-03 09:50:00
    Linux网卡驱动分析 Linux网卡驱动分析 学习应该是一个先把问题简单化,在把问题复杂化的过程。一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉。读Linux网卡驱动也是一样。那长长的源码夹杂着那些...
  • 利用Vmvare自动安装的虚拟网卡 配置Oracle的监听地址 TNSListener的监听地址配置成 虚拟机网卡的地址 重启Oracle和OracleTnsListener服务 PL/SQL连接测试 问题描述 我们在自己的笔记本,安装Oracle数据库进行...
  • linux网卡命名规则

    万次阅读 2018-08-11 16:15:53
    Linux系统的命名原来是eth0,eth1这样的形式,但是这个编号往往不一定准确对应网卡接口的物理顺序。 为解决这类问题,dell开发了biosdevname方案。 systemd v197版本中将dell的方案作了进一步的一般化拓展。 .....
  • 虚拟网卡TUN/TAP驱动程序设计原理

    千次阅读 2012-08-28 11:54:05
    本文将介绍tun/tap驱动的使用并分析虚拟网卡tun/tap驱动程序在linux环境下的设计思路。 tun/tap 驱动程序实现了虚拟网卡的功能,tun表示虚拟的是点对点设备,tap表示虚拟的是以太网设备,这两种设备针对网络包实施...
  • linux 虚拟

    2021-03-25 17:56:30
    另外介绍了一些其它的虚拟化技术, 比如Linux上操作系统级的虚拟化技术.  虚拟化把事物从一种形式改变为另一种形式. 计算机的虚拟化使单个计算机看起来像多个计算机或完全不同的计算机.  虚拟化技术也可以使多台...
  • 网卡驱动之虚拟网卡驱动编写

    千次阅读 2017-03-31 23:28:03
    我们来实现这么一个目的 我ping 3.3.3.4的时候,按理说如果是真实网卡的话,我们3.3.3.3的机器,和3.3.3.4的机器。ping 3.3.3.4的时候3.3.3.3的机器会把包发给...但是我们是虚拟网卡,并没有真正的网线,也没有3.3.3.4
  • 前一段时间,一直在找寻windows操作系统上的虚拟网卡接口,为了搭建隧道使用。但是windows操作系统不像Linux操作系统,它的代码不开源,导致这方面的资料很少,因此也找寻了很长时间,最终找到了两款开源项目的虚拟...
  • tap/tun 是 Linux 内核 2.4.x 版本之后实现的虚拟网络设备,不同于物理网卡靠硬件网卡实现,tap/tun 虚拟网卡完全由软件来实现,功能和硬件实现完全没有差别,它们都属于网络设备,都可以配置 IP,都归 Linux 网络...
  • 虚拟网卡性能压测

    2017-07-01 08:17:00
    根据openstack实际的部署方式,虚拟机网卡压测场景包括SRIOV(passthrough)、SRIOV+Macvtap(passthrough)、Vlan+Linux bridge、OVS+Linux Bridge,分别从协议类型(TCP/UDP)、Message Size方向压测虚拟机网卡的时延...
  • 声明:文本是看完韦... 在介绍本文之前,我想先对前面的知识做一下总结,我们知道Linux系统的设备分为字符设备(char device),块设备(block device),以及网络设备(network device)。字符设备是指存取时没...
  • M. Tim Jones, 顾问工程师, Emulex 简介: 虚拟化的含义丰富,应用...我们还将了解现有的其他一些虚拟化技术,例如 Linux 上操作系统的虚拟化。 发布日期: 2010 年 9 月 20 日 级别: 中级 访问情况 :

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 28,294
精华内容 11,317
关键字:

linux虚拟网卡开发

linux 订阅