我正在尝试创建一个用户空间网络堆栈以用于学习目的。
我想要做的是在装有 Windows 10 主机的 VirtualBox 机器上设置一个 Tap 设备。VirtualBox 在桥接网络上运行 Ubuntu 16.04。
我设置 Tap 设备的方式是通过在中创建一个 Tap /dev/net
:
sudo mknod /dev/net/tap c 10 200
我的程序正在运行tun_alloc
:
/*
* Taken from Kernel Documentation/networking/tuntap.txt
*/
int tun_alloc(char *dev)
{
struct ifreq ifr;
int fd, err;
/* Arguments taken by the function:
*
* char *dev: the name of an interface (or '\0'). MUST have enough
* space to hold the interface name if '\0' is passed
* int flags: interface flags (eg, IFF_TUN etc.)
*/
/* open the clone device */
if ((fd = open("/dev/net/tap", O_RDWR)) < 0)
{
printf("Cannot open TUN/TAP dev\n");
exit(1);
}
/* preparation of the struct ifr, of type "struct ifreq" */
memset(&ifr, 0, sizeof(struct ifreq));
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
* IFF_TAP - TAP device
*
* IFF_NO_PI - Do not provide packet information
*/
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
if (*dev)
{
/* if a device name was specified, put it in the structure; otherwise,
* the kernel will try to allocate the "next" device of the
* specified type */
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
/* try to create the device */
if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0)
{
printf("ERR: Could not ioctl tun: %s\n", strerror(errno));
close(fd);
return err;
}
/* if the operation was successful, write back the name of the
* interface to the variable "dev", so the caller can know
* it. Note that the caller MUST reserve space in *dev (see calling
* code below) */
strcpy(dev, ifr.ifr_name);
/* this is the special file descriptor that the caller will use to talk
* with the virtual interface */
return fd;
}
然后运行以下 shell 命令(来自程序):
ip link set dev tap0 up
ip route add dev tap0 192.168.1.0/24
ip address add dev tap0 local 192.168.1.21
当我运行这个时,我的ifconfig
节目:
enp0s3 Link encap:Ethernet HWaddr 08:00:27:2f:ba:f8
inet addr:192.168.1.36 Bcast:192.168.1.255 Mask:255.255.255.0
inet6 addr: fe80::445b:f137:4390:8b40/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:149 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1239 (1.2 KB) TX bytes:24836 (24.8 KB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:68 errors:0 dropped:0 overruns:0 frame:0
TX packets:68 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:5351 (5.3 KB) TX bytes:5351 (5.3 KB)
tap0 Link encap:Ethernet HWaddr e2:c8:48:94:af:9c
inet addr:192.168.1.21 Bcast:0.0.0.0 Mask:255.255.255.255
inet6 addr: fe80::e0c8:48ff:fe94:af9c/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
TX packets:51 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:42 (42.0 B) TX bytes:6078 (6.0 KB)
当我不运行该程序并且没有 tap0 接口时,我可以 ping 到我的主机,它也可以 ping 我。
但是当我运行此程序并且我的 Tap 设备已启动时,我突然与主机失去通信。此外,当我从主机 ping 到我的虚拟机时,我没有看到隧道读取任何数据包,事实上,当我检查我的 Windows 主机的 ARP 表时,我还看到enp0s3
接口 MAC 地址已从此 ARP 表中删除。
需要明确的是,我确实看到了在 Tap 设备上接收到的其他数据包,但不是来自我的主机外部。
这是我在主机内部测试 ARP 请求时看到的内容:
arping -I tap0 192.168.1.21
ARPING 192.168.1.21 from 192.168.1.21 tap0
Unicast reply from 192.168.1.21 [00:0C:29:6D:50:25] 0.545ms
Unicast reply from 192.168.1.21 [00:0C:29:6D:50:25] 0.733ms
现在我的问题是:
为什么在运行我的 Tap 设备时,我看不到任何来自外部的数据包?
为什么我也无法 ping 通我的其他 (enp0s3) 接口?这其中有什么关系?
答案1
您的 LAN 段有重叠:enp0s3
和tap0
都有 192.168.1.0/24。因此这行不通。
为您的tap0
接口提供一个不重叠的范围,例如 192.168.2.0/24。
如果你想创建自己的网络堆栈,我建议你阅读有关 LAN 段、广播域和为什么它们在不同的网络适配器上都必须不同。
如果您想学习网络,我还建议您先尝试使用网络命名空间和虚拟以太网对。这样,您将能够设置自己的“模拟”网络,并获得网络方面的实践经验。这将使编写自己的网络堆栈变得容易得多。甚至还有 GUI 可以为您设置此类网络。