跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C

Linux 内核虚拟网络设备驱动示例 vnet

Linux 内核虚拟网络设备驱动示例展示了如何编写一个名为 vnet 的模块。该模块模拟网络数据包的发送与接收过程,通过拦截发往真实网卡的数据并构造响应包来实现 ping 通功能。代码包含设备初始化、操作函数定义及 Makefile 编译脚本。适用于学习 Linux 网络子系统开发。

赛博行者发布于 2015/5/15更新于 2026/9/1070 浏览
Linux 内核虚拟网络设备驱动示例 vnet

Linux 虚拟网络设备驱动 (vnet)

源代码

#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/ip.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>

static struct net_device * virt_net_dev;
static netdev_tx_t virt_net_send_packet(struct sk_buff *skb,struct net_device *dev);
static const struct net_device_ops virt_net_ops = {
    .ndo_start_xmit = virt_net_send_packet,
};

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 netdev_tx_t virt_net_send_packet(struct sk_buff *skb,struct net_device *dev) {
    //static int cnt;
    //printk("virt_net has send %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); /* 数据全部发送出去后,唤醒网卡的队列 */
    virt_net_dev->stats.tx_packets++;
    virt_net_dev->stats.tx_bytes +=skb->len;
    return 0;
}

void virt_net_init(struct net_device *dev) {
    ether_setup(dev);
    dev->netdev_ops= &virt_net_ops; /* 2. 设置 */
    /* 设置 MAC 地址 */
    dev->dev_addr[0] = 0x08;
    dev->dev_addr[1] = 0x89;
    dev->dev_addr[2] = 0x89;
    dev->dev_addr[3] = 0x89;
    dev->dev_addr[4] = 0x89;
    dev->dev_addr[5] = 0x11;
    /* 设置下面两项才能 ping 通 */
    dev->flags |= IFF_NOARP;
    dev->features |= NETIF_F_NO_CSUM;
}

static int __init s3c_virt_net_init(void) {
    int ret;
    /* Init network device */
    virt_net_dev = alloc_netdev(0, "vnet%d", virt_net_init);
    if (!virt_net_dev) {
        printk("could not allocate device.\n");
        return -ENOMEM;
    }
    ret = register_netdev(virt_net_dev);
    return ret;
}

static void s3c_virt_net_exit(void) {
    unregister_netdev(virt_net_dev);
    free_netdev(virt_net_dev);
}

module_init(s3c_virt_net_init);
module_exit(s3c_virt_net_exit);
MODULE_LICENSE("GPL");

编译配置

ifneq ($(KERNELRELEASE),)
obj-m := vnet.o
else
KDIR := /boost/kernel/X7_kernel
#KDIR := /lib/modules/$(uname-r)*/build
all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif

目录

  1. Linux 虚拟网络设备驱动 (vnet)
  2. 源代码
  3. 编译配置

更多推荐文章

查看全部
  • EhViewer:开源免费安卓 E-Hentai 漫画浏览工具安装与使用指南
  • Python 3.14 安装教程:Windows/macOS/Linux 全平台指南
  • Rust 与 WebAssembly 实战:在浏览器与 Node.js 运行高性能代码
  • 基于百度天气数据的 WebGIS 空气质量可视化实践
  • OpenClaw 深度调教指南:5 步让 AI 助手真正能干活
  • SkyWalking 告警通知渠道集成:Webhook、Slack、钉钉、企业微信
  • 大模型开发与微调实战:基于 PyTorch 与 ChatGLM
  • llama.cpp 重大更新:内置 Web UI,性能对比 Ollama
  • GitHub Copilot Agent Mode 与 MCP 在 JetBrains 中的配置实战
  • DeepSeek 各版本演进与核心特性对比
  • Go 语言极简教程:Java 开发者视角入门实战
  • Linux GDB 调试指南
  • 无人机影像像素坐标转大地坐标
  • 基于 Go 语言构建命令行 AI 对话客户端实战
  • 大模型基础任务失误分析与提示词优化方案
  • Amazon SageMaker 部署 AIGC 应用:模型训练、优化与 Web 前端集成
  • AWPortrait-Z LoRA 强度与推理步数协同调优指南
  • LLM 申请 Hugging Face 访问令牌(以 Meta-Llama-3.1-8B-Instruct 为例)
  • 2026 RAG 演进:DeepSeek 与 Neo4j 构建企业级 GraphRAG 体系
  • 2024 年中国 AI 大模型场景探索及产业应用深度分析

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online