Chinaunix首页 | 论坛 | 博客
  • 博客访问: 31553
  • 博文数量: 14
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 155
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-07 10:01
文章分类
文章存档

2009年(14)

我的朋友

分类: LINUX

2009-10-07 11:23:44

学习了linux驱动程序,模仿tun写了一个虚拟网卡驱动,非常简陋
 
[虚拟网卡设备] <----> [字符设备] <----> [NIC]
设备虚拟网络卡设备,和字符设备,通过socket向虚拟网卡发送和接受的数据,可以通过字符设备进行读写.
通过一个用户空间程序,可以将字符设备和真实的网卡连接起来,并实现虚拟网卡和真实网卡的桥接
 

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>

#define DBG(x...) printk(x)
//#define DBG(x...)


#define VNIC_MINOR 200
#define READQ_SIZE 500
#define SETIFUP 0xff0001
#define SETIFDN 0xff0002
#define SETIFRUN 0xff0003

static LIST_HEAD(vnic_net_list);

struct vnic_struct{
    struct list_head list; //设备链表

    wait_queue_head_t read_head; //等待队列

    spinlock_t lock; //琐

    struct sk_buff_head readq; //收包队列

    struct net_device *dev; //net_device

    struct net_device_stats stats; //设备状态

     
     
};


loff_t no_chr_llseek(struct file *file, loff_t offset, int origin){

    return -ESPIPE;
}

static int vnic_chr_open(struct inode *inode, struct file *file){
    file->private_data = NULL;
    return 0;
}

static int vnic_chr_close(struct inode *inode, struct file *file){

    return 0;
}

static ssize_t vnic_chr_read(struct file * file, char __user * buf,
                size_t count, loff_t *pos){

    struct sk_buff *skb;
    struct vnic_struct *vnic = file->private_data;
    int len;
    DECLARE_WAITQUEUE(wait, current); //声明并初始化等待队列


    add_wait_queue(&vnic->read_head, &wait); //加入等待队列

     
    wait_event_interruptible(vnic->read_head, skb_queue_len(&vnic->readq)>0);
    if(vnic == NULL){
        DBG(KERN_INFO"vnic data wrong\n");
        return -1;
    }
    skb = skb_dequeue(&vnic->readq); //从收包队列中解包

    if(skb == NULL){
        DBG(KERN_INFO"no packet\n");
        return 0;
    }
    netif_wake_queue(vnic->dev); //重新唤醒收包队列(当队列满时,将停止收包,这边来唤醒)

    len = skb->len;
    if(copy_to_user(buf, skb->data, len)){ //提交给用户空间

        DBG(KERN_ERR"copy to use failed\n");
    }
    /*
        统计状态维护
    */

    vnic->stats.tx_packets++;
    vnic->stats.tx_bytes += len;
     
    kfree_skb(skb); //释放skb

    remove_wait_queue(&vnic->read_head, &wait); //移出等待队列

    schedule(); //放弃cpu

    return len;

}

static ssize_t vnic_chr_write(struct file * file, const char __user * buf,
                 size_t count, loff_t *pos){
    struct vnic_struct *vnic;
    struct sk_buff *skb;
    struct net_device *dev;
    int len;
    int align = 2;

    vnic = file->private_data;
    if(vnic == NULL){
        DBG(KERN_DEBUG"write data wrong\n");
        return -1;
    }
    dev = vnic->dev;
    len = count;
    if(len > dev->mtu + ETH_HLEN){ //包过大

        vnic->stats.rx_dropped++;
        DBG(KERN_INFO"packet too long\n");
        return -1;
    }
     
    skb = alloc_skb(len + align, GFP_KERNEL); //分配skb

    if(skb == NULL){
        vnic->stats.rx_dropped++;
        DBG(KERN_ERR"alloc skb failed\n");
        return -1;
    }
    skb_reserve(skb, align); //16bytes对齐

    skb_put(skb, len); //移动指针,准备数据copy

    if(copy_from_user(skb->data, buf, len)){ //用户空间copy数据

        kfree_skb(skb); //失败,释放skb

        DBG(KERN_INFO"copy from user failed\n");
        vnic->stats.rx_dropped++;
        return -1;
    }
    skb->dev = dev;
    skb->protocol = eth_type_trans(skb, dev); //解析以太协议

    netif_rx_ni(skb); //提交给上层协议栈

    dev->last_rx = jiffies; //last_rx维护


    /*
        设备状态维护
    */

    vnic->stats.rx_packets++;
    vnic->stats.rx_bytes += len;
     
    return len;
}

int vnic_net_open(struct net_device *dev){

    netif_start_queue(dev); //启动队列

    return 0;
}

int vnic_net_stop(struct net_device *dev){

    netif_stop_queue(dev); //停止队列

    return 0;
}

int vnic_net_tx(struct sk_buff *skb,
                            struct net_device *dev){
    struct vnic_struct *vnic = netdev_priv(dev);
    if(skb_queue_len(&vnic->readq) >= dev->tx_queue_len){ //队列是否满

        vnic->stats.tx_dropped++; //丢包

        kfree_skb(skb);
        netif_stop_queue(dev); //停止队列

        DBG(KERN_INFO"read queue is full\n");
    }else{
        skb_queue_tail(&vnic->readq, skb); //加入收包队列,等待处理

        dev->trans_start = jiffies;
    }
    wake_up_interruptible(&vnic->read_head); //唤醒等待队列

    DBG(KERN_DEBUG"tx queue: %d\n", skb_queue_len(&vnic->readq));
    return 0;
}

static struct net_device_stats *vnic_net_stats(struct net_device *dev)
{
    struct vnic_struct *vnic = netdev_priv(dev);
    return &vnic->stats;
}


void vnic_setup(struct net_device *dev){
    struct vnic_struct *vnic = netdev_priv(dev); //获取net_device私有数据

     
    ether_setup(dev); //设置设备为以太网设备,定义部分以太网络的参数

    /*
        以下为设置网络设备的操作函数
    */

    dev->open = vnic_net_open;
    dev->stop = vnic_net_stop;
    dev->hard_start_xmit = vnic_net_tx;
    dev->get_stats = vnic_net_stats;

     
    random_ether_addr(dev->dev_addr); //随机源地址

    dev->tx_queue_len = READQ_SIZE; //收包队列长度

    vnic->dev = dev; //设置私有数据的net_device成员

    memset(&vnic->stats, 0, sizeof(struct net_device_stats));
    spin_lock_init(&vnic->lock);
    init_waitqueue_head(&vnic->read_head); //初始化等待队列

    skb_queue_head_init(&vnic->readq); //初始化收包队列

    spin_lock(&vnic->lock);
    list_add(&vnic->list, &vnic_net_list); //将新建vnic_struct加入到设备链表

    spin_unlock(&vnic->lock);
}

struct net_device *setup_dev(void){
    struct net_device *dev;

    dev = alloc_netdev(sizeof(struct vnic_struct), "vnic%d", vnic_setup); //分配net_device,初始化函数vnic_setup

    if(dev == NULL){
        DBG(KERN_ERR"alloc net device failed\n");
        return NULL;
    }

    if(register_netdev(dev) != 0){ //注册网络设备

        DBG(KERN_ERR"register net device failed\n");
        return NULL;
    }
     
    return dev;
}

int shutdown_dev(void){
    struct vnic_struct *vnic, *next;
     
    list_for_each_entry_safe(vnic, next, &vnic_net_list, list){ //遍历所有vnic_struct

        spin_lock(&vnic->lock);
        skb_queue_purge(&vnic->readq); //清空收包队列

        list_del(&vnic->list); //将当前vnic_struct从队列中删除

        unregister_netdev(vnic->dev); //注销网络设备

        free_netdev(vnic->dev); //释放网络设备

        spin_unlock(&vnic->lock);
    }

    return 0;
}

static int vnic_chr_ioctl(struct inode *inode, struct file *file,
             unsigned int cmd, unsigned long arg){
    int ret = 0;
    struct net_device *dev;
    struct vnic_struct *vnic;
     
     
    switch(cmd){
        case SETIFUP: //建立虚拟网卡

            if((dev = setup_dev()) == NULL){
                DBG(KERN_DEBUG"setup net device failed\n");
                ret = -EFAULT;
            }else{
                file->private_data = netdev_priv(dev);
            }
            break;
        case SETIFDN: //关闭虚拟网卡

            if(shutdown_dev() == -1){
                DBG(KERN_DEBUG"shutdown net device failed\n");
                ret = -EFAULT;
            }
            break;
    }
     
    return ret;
}


struct file_operations vnic_ops = {
    .owner = THIS_MODULE,
    .llseek = no_chr_llseek,
    .open = vnic_chr_open,
    .release = vnic_chr_close,
    .read = vnic_chr_read,
    .write = vnic_chr_write,
    .ioctl = vnic_chr_ioctl
     
};

static struct miscdevice vnic_misc = {
    .minor = VNIC_MINOR, //混合设备minor号

    .name = "vnic", //设备名称

    .fops = &vnic_ops //操作函数定义

     
};


int vnic_chr_init(void){
    int ret = 0;

    ret = misc_register(&vnic_misc); //注册为混合设备,major = 10

    if(ret){
        DBG(KERN_INFO"misc register failed, device name:%s\n", vnic_misc.name);
    }else{
        DBG(KERN_INFO"misc register OK, device: %s, minor: %d\n", vnic_misc.name, vnic_misc.minor);
    }
    return ret;
     
}

void vnic_chr_exit(void){

    misc_deregister(&vnic_misc); //注销混合设备

    shutdown_dev(); //关闭网络设备

    DBG(KERN_INFO"misc deregister OK\n");
     
}

MODULE_AUTHOR("wzq");
MODULE_LICENSE("GPL");
module_init(vnic_chr_init);
module_exit(vnic_chr_exit);


阅读(1175) | 评论(0) | 转发(0) |
0

上一篇:sockstress 压力测试工具

下一篇:nfshell

给主人留下些什么吧!~~