同事们,下午好。请帮助我了解以下情况。我想在用户空间处理以太网帧。要“接收”一帧,我想使用 Tap-device (这样的任务,Raw-socket 不适合这种情况)。其实是一个问题。我使用以下命令创建 Tap-device:
sudo ip tuntap add dev tap0 mode tap
sudo ip address add 10.10.10.3/30 dev tap0
我在间隔中启动了一个自写的软件部分(关于它在下面)。接下来做
sudo ip link set tap0 up
ip address show tap0
结果,我在控制台中得到以下输出:
7: tap0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
link/ether ba:ba:18:ce:bb:66 brd ff:ff:ff:ff:ff:ff
inet 10.10.10.3/30 scope global tap0
valid_lft forever preferred_lft forever
同时,使用该程序,我将创建的 tap-device 与“/dev/net/tun”文件相关联。程序文本:
#include <arpa/inet.h>
#include <cstring>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/if_tun.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stropts.h>
#include <syslog.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int tun_alloc(char *dev, short flags);
int main() {
char tun_name[IFNAMSIZ];
char buffer[1500];
printf("Programm is starting\n");
/* Подключаюсь к tap-device */
strcpy(tun_name, "tap0");
int tun_fd = tun_alloc(tun_name, IFF_TAP); /* tun interface */
if (tun_fd < 0) {
perror("Allocating interface");
exit(1);
}
printf("Interface number = %d \n", tun_fd);
printf("Start reading loop \n");
while (1) {
printf("Whating for data... \n");
int nread = read(tun_fd, buffer, sizeof(buffer));
if (nread < 0) {
perror("Reading from interface");
close(tun_fd);
exit(1);
}
/* Здесь обрабатывать полученный фрейм */
printf("Read %d bytes from device %s\n", nread, tun_name);
}
return 0;
}
int tun_alloc(char *dev, short flags) {
printf("Entering tun_allocate...\n");
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
/* Открываем файловый дескриптор вирт.устройства,
с которого ожидаю получить Ethernet-frame*/
if ((fd = open(clonedev, O_RDWR)) < 0) {
perror("open(clonedev, O_RDWR)");
return fd;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags;
if (*dev) {
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
/* пытаюсь увязать файл и Tap-device */
if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
close(fd);
perror("ioctl(fd, TUNSETIFF, (void *)&ifr)");
return err;
}
strcpy(dev, ifr.ifr_name);
printf("Everything seems OK! \n");
return fd;
}
在启动时,我开始收到一堆服务以太网帧,也就是说,一切似乎都在工作。但最好分析“the”数据包。
为此,我启动了一个简单的 UDP 发送器,我将它放在地址为 192.168.44.128:9999 的设备上,并开始向 10.10.10.3:9999 发送数据。
Eeeee ...在打开的文件描述符fd上我什么也没得到。同时,为了好玩,我尝试使用 UDP-listener 程序(通常的 open ... bind ... read ... cout 集)收听 10.10.10.3:9999。所有数据都在那里。
我不明白为什么我在fd上没有收到以太网帧,尽管根据 TunTap 手册,我应该收到它们,据我所知。
请告知为什么会这样。我有错误的代码吗?我误解了 TunTap 的概念吗?Tap-device的错误创建?误解了网络堆栈?
我尝试通过创建 Tun-device 来解决类似的问题,结果类似。所有变态都发生在 VM Ware Workstation 15 +5.4.0-42-generic #46~18.04.1-Ubuntu SMP Fri Jul 10 07:21:24 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux 下。
是的。Tun/Tap 用于创建 VPN 连接。发送到分配给 tap 接口的 L3 地址的数据包实际上并没有进入它:它们由内核处理并立即传送到它们所寻址的程序(如果有的话)。需要此地址才能从 VPN 连接的另一端接收数据。
那些。为了通过 tap 接口发送某些内容,您需要将数据发送到它后面的子网 (
10.10.10.0/30),例如,发送到10.10.10.1.但是由于 tap 模拟 L2 接口,那么 UDP/IP 数据包也不会“就这样”消失:为了将其发送到内核,您不仅需要知道 L3(IP)地址,还需要知道 L2( MAC) 向其发送数据包的接口的地址。为了找出答案,使用了ARP协议。那。在当前版本中,这只会导致发送一些 arp 请求,当然,这些请求仍然没有得到答复。要在接口上查看 UDP 数据包本身,您可以,例如:
¹ 请参阅此处的 Google 膝盖示例。