学习了驱动程序,模仿tun写了一个虚拟网卡驱动,非常简陋
[虚拟网卡设备]??[字符设备]??[NIC]
设备虚拟网络卡设备,和字符设备,通过socket向虚拟网卡发送和接受的数据,可以通过字符设备进行读写.
通过一个用户空间程序,可以将字符设备和真实的网卡连接起来,并实现虚拟网卡和真实网卡的桥接
#include /module.h>
#include /errno.h>
#include /kernel.h>
#include /miscdevice.h>
#include /init.h>
#include /major.h>
#include /slab.h>
#include /fcntl.h>
#include /skbuff.h>
#include /netdevice.h>
#include /etherdevice.h>
#include /if.h>
#include /if_arp.h>
#include /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);
本文来自ChinaUnix博客,
查看原文请点: