IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 虚拟网卡驱动例程 -> 正文阅读

[系统运维]虚拟网卡驱动例程

#include<linux/module.h>
#include<linux/sched.h>
#include<linux/kernel.h>
#include<linux/slab.h>
#include<linux/errno.h>
#include<linux/types.h>
#include<linux/interrupt.h>
#include<linux/in.h>
#include<linux/netdevice.h>
#include<linux/etherdevice.h>
#include<linux/ip.h>
#include<linux/tcp.h>
#include<linux/skbuff.h>
#include<linux/if_ether.h>
#include<linux/in6.h>
#include<asm/uaccess.h>
#include<asm/checksum.h>
#include<linux/platform_device.h>

#define  MAC_AUTO
static struct net_device *vir_net_devs;

struct vir_net_priv {
    struct net_device_stats stats;      //有用的统计信息
    int status;                         //网络设备的状态信息,是发完数据包,还是接收到网络数据包
    int rx_packetlen;                   //接收到的数据包长度
    u8 *rx_packetdata;                  //接收到的数据
    int tx_packetlen;                   //发送的数据包长度
    u8 *tx_packetdata;                  //发送的数据
    struct sk_buff *skb;                //socket buffer结构体,网络各层之间传送数据都是通过这个结构体来实现的
    spinlock_t lock;                    //自旋锁
};

/*网络设备开启时会执行该函数*/
int vir_net_open(struct net_device *dev) {
#ifndef MAC_AUTO
    int i;
    for (i=0; i<6; i++) {
        dev->dev_addr[i] = 0xaa;
    }
#else
    random_ether_addr(dev->dev_addr);
#endif
    /*打开传输队列进行数据传输*/
    netif_start_queue(dev);
    printk("vir_net_open\n");
    return 0;
}

/*关闭的时候,关闭队列*/
int vir_net_release(struct net_device *dev) {
    /*停止发送数据*/
    netif_stop_queue(dev);
    printk("vir_net_release\n");
    return 0;
}

/*接包函数,有数据过来时,中断执行*/
void vir_net_rx(struct net_device *pdev, int len, unsigned char *buf) {
    struct sk_buff *skb;
    struct vir_net_priv *priv = (struct vir_net_priv *) pdev->ml_priv;
    skb = dev_alloc_skb(len+2);//分配一个socket buffer,并且初始化skb->data,skb->tail和skb->head
    if(!skb) {
        printk("gecnet rx: low on mem - packet dropped\n");
        priv->stats.rx_dropped++;
        return;
    }
    skb_reserve(skb, 2); /* align IP on 16B boundary */ 
    memcpy(skb_put(skb, len), buf, len);//skb_put是把数据写入到socket buffer
    /* Write metadata, and then pass to the receive level */
    skb->dev = pdev;
    skb->protocol = eth_type_trans(skb, pdev);//返回的是协议号
    skb->ip_summed = CHECKSUM_UNNECESSARY; //此处不校验
    priv->stats.rx_packets++;//接收到包的个数+1

    priv->stats.rx_bytes += len;//接收到包的长度
    printk("vir_net_rx\n");
    netif_rx(skb);//通知内核已经接收到包,并且封装成socket buffer传到上层
    return;
}

/*模拟硬件发送数据*/
void vir_net_hw_tx(char *buf, int len, struct net_device *dev) {
    struct net_device *dest;//目标设备结构体,net_device存储一个网络接口的重要信息,是网络驱动程序的核心
    struct vir_net_priv *priv;

    if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {
        printk("vir_net: packet too short (%i octets)\n", len);
        return;
    }
    dest = vir_net_devs;
    priv = (struct vir_net_priv *)dest->ml_priv;
    priv->rx_packetlen = len;
    priv->rx_packetdata = buf;

    printk("vir_net_hw_tx\n");
    dev_kfree_skb(priv->skb);
}


/*发包函数, 上层有数据发送时,该函数会被调用*/
int vir_net_tx(struct sk_buff *skb, struct net_device *pdev) {
    int len;
    char *data;
    struct vir_net_priv *priv = (struct vir_net_priv *)pdev->ml_priv;
    if(skb == NULL) {
        printk("net_device = %p, skb = %p\n", pdev, skb);
        return 0;
    }
    /*ETH_ZLEN是所发的最小数据包的长度*/
    len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
    /*将要发送的数据包中数据部分*/
    data = skb->data;
    priv->skb = skb;
    /*调用硬件接口进行数据的发送*/
    vir_net_hw_tx(data, len, pdev);
    printk("vir_net_tx, pdev = %p\n", pdev);
    return 0; 
}

/*设备初始化函数*/
int vir_net_device_init(struct net_device *pdev) {
    /*填充一些以太网中的设备结构体的项*/
    ether_setup(pdev);
    /*keep the default flags, just add NOARP */
    pdev->flags |= IFF_NOARP;
    /*为priv分配内存*/
    pdev->ml_priv = kmalloc(sizeof(struct vir_net_priv), GFP_KERNEL);
    if (pdev->ml_priv == NULL){
        return -ENOMEM;
    }
    memset(pdev->ml_priv, 0, sizeof(struct vir_net_priv));
    spin_lock_init(&((struct vir_net_priv *)pdev->ml_priv)->lock);
    printk("vir_net_device_init, pdev = %p\n", pdev);
    return 0;
}


/*结构体填充*/
static const struct net_device_ops vir_net_netdev_ops = {
    .ndo_open       = vir_net_open,       //打开网卡 对应ifconfig xx up
    .ndo_stop       = vir_net_release,    //关闭网卡 对应ifconfig xx down
    .ndo_start_xmit = vir_net_tx,         //开启数据包传输(对应上层要发送数据时)
    .ndo_init       = vir_net_device_init,       //初始化网卡硬件
};


/**/
static void vir_plat_net_release(struct device *pdev) {
    printk("vir_plat_net_release, pdev = %p\n", pdev);
}


/*匹配*/
static int vir_net_probe(struct platform_device *pdev) {
    int result = 0;
    /*vir_net_devs结构体相当于一个虚拟的网络设备*/
    vir_net_devs = alloc_etherdev(sizeof(struct net_device));
    vir_net_devs->netdev_ops = &vir_net_netdev_ops;
    strcpy(vir_net_devs->name, "net_0");
    /*上面填充了3项,如果是真实的网卡会填充更多,然后
    使用register_netdev进行注册,net/core,注册好了以后
    内核当中就会有这个设备了,当这个网络设备up以后就会进入open函数*/
    if ((result = register_netdev(vir_net_devs))) {
        printk("vir_net: error %i registering device \"%s\"\n", result, vir_net_devs->name);
    }
    printk("vir_net_probe, pdev = %p\n", pdev);
    return 0;
}

/*设备移除函数*/
static int  vir_net_remove(struct platform_device *pdev) {
    kfree(vir_net_devs->ml_priv);
    unregister_netdev(vir_net_devs);
    return 0;
}

/*结构体填充*/
static struct platform_device vir_net= {
    .name = "vir_net",
    .id   = -1,
    .dev  = {
    .release = vir_plat_net_release,
    },
};

/*结构体填充*/
static struct platform_driver vir_net_driver = {
    .probe  = vir_net_probe,
    .remove  = vir_net_remove,
    .driver  = {
    .name = "vir_net",    /*这里的name和上面那个结构体的name如果匹配就会执行probe函数*/
    .owner = THIS_MODULE,
    },
};

/*模块入口函数*/
static int __init vir_net_init(void) {
    printk("vir_net_init\n");
    platform_device_register(&vir_net);
    return platform_driver_register(&vir_net_driver);
}

/*模块退出函数*/
static void __exit vir_net_exit(void) {
    printk("vir_net_exit\n");
    platform_driver_unregister(&vir_net_driver);
    platform_device_unregister(&vir_net);
}

module_init(vir_net_init);
module_exit(vir_net_exit);
MODULE_LICENSE("GPL");

makefile

CONFIG_MODULE_SIG = n
obj-m+=virnet.o
all:
        make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
clean:
        make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean

在使用sudo ifconfig xxx up/down和sudo insmod xxx,sudo rmmod xxx命令时,输出的结果可以通过开启另外一个终端观察,tail -f /var/log/kern.log,查看内核日志的输出,从而观察到各个命令的执行时,他们会去执行哪一个函数。
在这里插入图片描述

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 19:10:50  更:2022-03-30 19:12:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 23:30:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码