#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);
|