是的,当使用 TUN 设备时,默认情况下发送到用户空间的数据包会附加额外的头信息,这些头信息通常称为协议头(Protocol Information, PI)。这使得用户空间应用程序可以获取一些额外的信息,例如数据包的类型和长度。
示例
假设你在 Linux 系统上创建了一个 TUN 设备,并且没有使用 IFF_NO_PI
标志。在这种情况下,从 TUN 设备读取数据时,接收到的数据将包含以下内容:
- 协议头: 包含有关传输的数据包的信息,比如包的类型、长度等。
- IP 数据包: 实际传输的数据,通常是 IPv4 或 IPv6 的数据包。
例子
- 创建一个 TUN 设备(未使用
IFF_NO_PI
):
struct ifreq ifr;
int tun_fd = open("/dev/net/tun", O_RDWR);
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN; // 设置为 TUN 类型
ioctl(tun_fd, TUNSETIFF, (void *) &ifr);
- 从 TUN 设备读取数据:
char buffer[2048];
int nread = read(tun_fd, buffer, sizeof(buffer));
// buffer 中的前几个字节将包含协议头信息
// 假设 PI 是两个字节,那么实际 IP 数据包从第3个字节开始
struct tun_pi *pi_header = (struct tun_pi *)buffer; // 获取协议头指针
struct iphdr *ip_header = (struct iphdr *)(buffer + sizeof(struct tun_pi)); // 获取 IP 数据包指针
printf("Packet type: %d\n", ntohs(pi_header->flags)); // 打印标志(如类型)
printf("IP packet length: %d\n", nread - sizeof(struct tun_pi)); // IP 数据包的长度
在这个例子中,buffer
将包含两部分内容:首先是额外添加的协议头(例如 tun_pi
),然后是实际的 IP 数据包。通过解析这些数据,你可以提取出关于接收的数据包的信息。
如果你希望跳过这些协议头并直接处理 IP 数据包,可以在创建 TUN 设备时使用 IFF_NO_PI
标志。例如:
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; // 设置为 TUN 类型,不附加 PI
在这种情况下,从 TUN 设备读取数据时,你只会收到原始 IP 数据包,而不会有额外的协议头。
内容由零声教学AI助手提供,问题来源于学员提问