linux tap是linux内核提供的一种虚拟网络接口,属于虚拟网络设备的范畴,核心作用是在用户态程序和内核网络栈之间建立数据链路层的通信通道。

linux tap的核心特性
tap设备的核心特点可以从以下几个维度理解:
- 工作层级:tap工作在OSI模型的数据链路层,能够处理以太网帧,因此可以被当作一块虚拟的以太网网卡使用。
- 虚拟属性:tap没有对应的物理硬件,完全由内核软件模拟实现,其状态和行为可以和物理网卡保持一致。
- 双向通信:tap设备既可以接收内核网络栈下发的数据包,也可以把用户态程序提交的以太网帧注入到内核网络栈中。
- 字符设备关联:每个tap设备都对应一个/dev/net/tun字符设备,用户态程序通过操作这个设备文件来完成数据包的读写。
linux tap的工作原理
tap设备的通信流程主要分为两步:
- 用户态程序通过系统调用打开/dev/net/tun设备,然后通过ioctl操作创建对应的tap虚拟接口,同时可以设置接口的IP、MAC等参数。
- 创建完成后,用户态程序可以通过read从tap设备读取内核下发的数据包,也可以通过write向tap设备写入需要上传到内核的以太网帧,内核网络栈会像处理物理网卡的数据包一样处理这些帧。
我们可以用一个简单的流程表示:
用户态程序 ↔ tap字符设备 ↔ tap虚拟网卡 ↔ 内核网络栈 ↔ 其他网络接口/应用
linux tap的常见使用场景
虚拟机网络
在QEMU、KVM等虚拟化场景中,tap设备是最常用的虚拟机网络后端。虚拟机的虚拟网卡会把数据包发送到宿主机的tap设备,再由宿主机的内核网络栈转发到外部网络,实现虚拟机和外部网络的通信。
VPN应用
很多用户态VPN程序会使用tap设备作为网络接口,VPN程序把加密后的网络数据解密后写入tap设备,内核就可以把这些数据当作普通的网络流量进行处理,实现虚拟专用网络的接入。
容器网络测试
在测试自定义容器网络方案时,我们可以通过创建tap设备模拟容器内的网络接口,快速验证数据链路层的转发逻辑,不需要完整的容器运行环境。
linux tap的简单使用示例
创建tap设备
在linux系统中,我们可以通过ip命令或者tunctl工具创建tap设备,以下是使用ip命令的示例:
# 创建名为tap0的tap设备 ip tuntap add mode tap tap0 # 为tap0设置IP地址 ip addr add 192.168.10.1/24 dev tap0 # 启动tap0接口 ip link set tap0 up
用户态读写tap设备的简单代码
以下是C语言实现的简单tap设备读写示例,展示如何打开tap设备并读取数据:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_tun.h>
// 打开tap设备的函数
int open_tap(const char *dev_name) {
int fd;
struct ifreq ifr;
// 打开tun/tap字符设备
fd = open("/dev/net/tun", O_RDWR);
if (fd < 0) {
perror("open /dev/net/tun failed");
return -1;
}
// 初始化ifreq结构体,设置设备名称为tap0,模式为TAP
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
strncpy(ifr.ifr_name, dev_name, IFNAMSIZ - 1);
// 通过ioctl创建tap设备
if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
perror("ioctl TUNSETIFF failed");
close(fd);
return -1;
}
printf("open tap device %s successn", ifr.ifr_name);
return fd;
}
int main() {
int tap_fd;
char buf[2048];
int nread;
// 打开tap0设备
tap_fd = open_tap("tap0");
if (tap_fd < 0) {
return 1;
}
// 循环读取tap设备的数据
while (1) {
nread = read(tap_fd, buf, sizeof(buf));
if (nread < 0) {
perror("read tap device failed");
break;
}
printf("read %d bytes from tap devicen", nread);
// 这里可以添加自定义的数据处理逻辑
}
close(tap_fd);
return 0;
}
tap和tun的区别
很多用户会把tap和tun混淆,两者都是linux的虚拟网络设备,核心区别如下:
| 对比维度 | tap设备 | tun设备 |
|---|---|---|
| 工作层级 | 数据链路层,处理以太网帧 | 网络层,处理IP数据包 |
| 模拟对象 | 以太网网卡 | 网络层接口 |
| 数据包格式 | 包含MAC头的完整以太网帧 | 不包含链路层头的IP数据包 |
| 适用场景 | 虚拟机网络、以太网层VPN | IP层VPN、路由转发场景 |
如果需要处理二层网络相关的逻辑,应该优先选择tap设备,如果只需要处理三层IP流量,tun设备会更合适。