☀️苏州程序大白解析Linux 中的虚拟网络接口☀️《❤️记得收藏❤️》
Posted 苏州程序大白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了☀️苏州程序大白解析Linux 中的虚拟网络接口☀️《❤️记得收藏❤️》相关的知识,希望对你有一定的参考价值。
☀️苏州程序大白解析Linux 中的虚拟网络接口☀️《❤️记得收藏❤️》
目录
🏳️🌈开讲啦!!!!🏳️🌈苏州程序大白🏳️🌈 |
🌟博主介绍
💂 个人主页:苏州程序大白
🤟作者介绍:中国DBA联盟(ACDU)成员,CSDN全国各地程序猿(媛)聚集地管理员。目前从事工业自动化软件开发工作。擅长C#、Java、机器视觉、底层算法等语言。2019年成立柒月软件工作室。
💬如果文章对你有帮助,欢迎关注、点赞、收藏(一键三连)和C#、Halcon、python+opencv、VUE、各大公司面试等一些订阅专栏哦
🎗️ 承接各种软件开发项目
💅 有任何问题欢迎私信,看到会及时回复
👤 微信号:stbsl6,微信公众号:苏州程序大白
🎯 想加入技术交流群的可以加我好友,群里会分享学习资料
前言
注意: 本文中使用 ip 命令
创建或修改的任何网络配置,都是未持久化的,主机重启即消失。
Linux 具有强大的虚拟网络能力,这也是 openstack 网络、docker 容器网络以及 kubernetes 网络等虚拟网络的基础。
这里介绍 Linux 常用的虚拟网络接口类型:TUN/TAP、bridge、veth、ipvlan/macvlan、vlan 以及 vxlan/geneve。
tun/tap 虚拟网络接口
tun/tap
是操作系统内核中的虚拟网络设备,他们为用户层程序提供数据的接收与传输。
普通的物理网络接口如 eth0,它的两端分别是内核协议栈和外面的物理网络。
而对于 TUN/TAP 虚拟接口如 tun0,它的一端一定是连接的用户层程序,另一端则视配置方式的不同而变化,可以直连内核协议栈,也可以是某个 bridge(后面会介绍)。 Linux 通过内核模块 TUN 提供 tun/tap 功能
,该模块提供了一个设备接口 /dev/net/tun 供用户层程序读写,用户层程序通过 /dev/net/tun 读写主机内核协议栈的数据。
> modinfo tun
filename: /lib/modules/5.13.6-1-default/kernel/drivers/net/tun.ko.xz
alias: devname:net/tun
alias: char-major-10-200
license: GPL
author: (C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>
description: Universal TUN/TAP device driver
...
> ls /dev/net/tun
/dev/net/tun
一个 TUN 设备的示例图如下:
+----------------------------------------------------------------------+
| |
| +--------------------+ +--------------------+ |
| | User Application A | | User Application B +<-----+ |
| +------------+-------+ +-------+------------+ | |
| | 1 | 5 | |
|...............+......................+...................|...........|
| ↓ ↓ | |
| +----------+ +----------+ | |
| | socket A | | socket B | | |
| +-------+--+ +--+-------+ | |
| | 2 | 6 | |
|.................+.................+......................|...........|
| ↓ ↓ | |
| +------------------------+ +--------+-------+ |
| | Network Protocol Stack | | /dev/net/tun | |
| +--+-------------------+-+ +--------+-------+ |
| | 7 | 3 ^ |
|................+...................+.....................|...........|
| ↓ ↓ | |
| +----------------+ +----------------+ 4 | |
| | eth0 | | tun0 | | |
| +-------+--------+ +-----+----------+ | |
| 10.32.0.11 | | 192.168.3.11 | |
| | 8 +---------------------+ |
| | |
+----------------+-----------------------------------------------------+
↓
Physical Network
因为 TUN/TAP 设备的一端是内核协议栈,显然流入 tun0 的数据包是先经过本地的路由规则匹配的。
路由匹配成功,数据包被发送到 tun0 后,tun0 发现另一端是通过 /dev/net/tun 连接到应用程序 B,就会将数据丢给应用程序 B。
应用程序对数据包进行处理后,可能会构造新的数据包,通过物理网卡发送出去。比如常见的 VPN 程序就是把原来的数据包封装/加密一遍,再发送给 VPN 服务器。
C 语言编程测试 TUN 设备
为了使用 tun/tap 设备,用户层程序需要通过系统调用打开 /dev/net/tun 获得一个读写该设备的文件描述符(FD),并且调用 ioctl() 向内核注册一个 TUN 或 TAP 类型的虚拟网卡(实例化一个 tun/tap 设备),其名称可能是 tun0/tap0 等。
此后,用户程序可以通过该 TUN/TAP 虚拟网卡与主机内核协议栈(或者其他网络设备)交互。当用户层程序关闭后,其注册的 TUN/TAP 虚拟网卡以及自动生成的路由表相关条目都会被内核释放。
可以把用户层程序看做是网络上另一台主机,他们通过 tun/tap 虚拟网卡相连。
一个简单的 C 程序示例如下,它每次收到数据后,都只单纯地打印一下收到的字节数:
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include<stdlib.h>
#include<stdio.h>
int tun_alloc(int flags)
{
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
// 打开 tun 文件,获得 fd
if ((fd = open(clonedev, O_RDWR)) < 0) {
return fd;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags;
// 向内核注册一个 TUN 网卡,并与前面拿到的 fd 关联起来
// 程序关闭时,注册的 tun 网卡及自动生成的相关路由策略,会被自动释放
if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {
close(fd);
return err;
}
printf("Open tun/tap device: %s for reading...\\n", ifr.ifr_name);
return fd;
}
int main()
{
int tun_fd, nread;
char buffer[1500];
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
* IFF_TAP - TAP device
* IFF_NO_PI - Do not provide packet information
*/
tun_fd = tun_alloc(IFF_TUN | IFF_NO_PI);
if (tun_fd < 0) {
perror("Allocating interface");
exit(1);
}
while (1) {
nread = read(tun_fd, buffer, sizeof(buffer));
if (nread < 0) {
perror("Reading from interface");
close(tun_fd);
exit(1);
}
printf("Read %d bytes from tun/tap device\\n", nread);
}
return 0;
}
接下来开启三个终端窗口来测试上述程序,分别运行上面的 tun 程序、tcpdump 和 iproute2 指令。
首先通过编译运行上述 c 程序,程序会阻塞住,等待数据到达:
# 编译,请忽略部分 warning
> gcc mytun.c -o mytun
# 创建并监听 tun 设备需要 root 权限
> sudo mytun
Open tun/tap device: tun0 for reading...
现在使用 iproute2 查看下链路层设备:
# 能发现最后面有列出名为 tun0 的接口,但是状态为 down
❯ ip addr ls
......
3: wlp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether c0:3c:59:36:a4:16 brd ff:ff:ff:ff:ff:ff
inet 192.168.31.228/24 brd 192.168.31.255 scope global dynamic noprefixroute wlp4s0
valid_lft 41010sec preferred_lft 41010sec
inet6 fe80::4ab0:130f:423b:5d37/64 scope link noprefixroute
valid_lft forever preferred_lft forever
7: tun0: <POINTOPOINT,MULTICAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 500
link/none
# 为 tun0 设置 ip 地址,注意不要和其他接口在同一网段,会导致路由冲突
> sudo ip addr add 172.21.22.23/24 dev tun0
# 启动 tun0 这个接口,这一步会自动向路由表中添加将 172.21.22.23/24 路由到 tun0 的策略
> sudo ip link set tun0 up
#确认上一步添加的路由策略是否存在
❯ ip route ls
default via 192.168.31.1 dev wlp4s0 proto dhcp metric 600
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.21.22.0/24 dev tun0 proto kernel scope link src 172.21.22.23
192.168.31.0/24 dev wlp4s0 proto kernel scope link src 192.168.31.228 metric 600
# 此时再查看接口,发现 tun0 状态为 unknown
> ip addr ls
......
8: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 500
link/none
inet 172.21.22.23/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::3d52:49b5:1cf3:38fd/64 scope link stable-privacy
valid_lft forever preferred_lft forever
# 使用 tcpdump 尝试抓下 tun0 的数据,会阻塞在这里,等待数据到达
> tcpdump -i tun0
现在再启动第三个窗口发点数据给 tun0,持续观察前面 tcpdump
和 mytun
的日志:
# 直接 ping tun0 的地址,貌似有问题,数据没进 mytun 程序,而且还有响应
❯ ping -c 4 172.21.22.23
PING 172.21.22.23 (172.21.22.23) 56(84) bytes of data.
64 bytes from 172.21.22.23: icmp_seq=1 ttl=64 time=0.167 ms
64 bytes from 172.21.22.23: icmp_seq=2 ttl=64 time=0.180 ms
64 bytes from 172.21.22.23: icmp_seq=3 ttl=64 time=0.126 ms
64 bytes from 172.21.22.23: icmp_seq=4 ttl=64 time=0.141 ms
--- 172.21.22.23 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3060ms
rtt min/avg/max/mdev = 0.126/0.153/0.180/0.021 ms
# 但是 ping 该网段下的其他地址,流量就会被转发给 mytun 程序,因为 mytun 啥数据也没回,自然丢包率 100%
# tcpdump 和 mytun 都会打印出相关日志
❯ ping -c 4 172.21.22.26
PING 172.21.22.26 (172.21.22.26) 56(84) bytes of data.
--- 172.21.22.26 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3055ms
下面给出 mytun 的输出:
Read 84 bytes from tun/tap device
Read 84 bytes from tun/tap device
Read 84 bytes from tun/tap device
Read 84 bytes from tun/tap device
以及 tcpdump 的输出:
00:22:03.622684 IP (tos 0x0, ttl 64, id 37341, offset 0, flags [DF], proto ICMP (1), length 84)
172.21.22.23 > 172.21.22.26: ICMP echo request, id 11, seq 1, length 64
00:22:04.633394 IP (tos 0x0, ttl 64, id 37522, offset 0, flags [DF], proto ICMP (1), length 84)
172.21.22.23 > 172.21.22.26: ICMP echo request, id 11, seq 2, length 64
00:22:05.653356 IP (tos 0x0, ttl 64, id 37637, offset 0, flags [DF], proto ICMP (1), length 84)
172.21.22.23 > 172.21.22.26: ICMP echo request, id 11, seq 3, length 64
00:22:06.677341 IP (tos 0x0, ttl 64, id 37667, offset 0, flags [DF], proto ICMP (1), length 84)
172.21.22.23 > 172.21.22.26: ICMP echo request, id 11, seq 4, length 64
TUN 与 TAP 的区别
TUN 和 TAP 的区别在于工作的网络层次不同,用户程序通过 TUN 设备只能读写网络层的 IP 数据包,而 TAP 设备则支持读写链路层的数据包(通常是以太网数据包,带有 Ethernet headers)。
TUN 与 TAP 的关系,就类似于 socket 和 raw socket。
TUN/TAP 应用最多的场景是 VPN 代理,比如:
1、clash: 一个支持各种规则的隧道,也支持 TUN 模式。
2、tun2socks: 一个全局透明代理,和 VPN 的工作模式一样,它通过创建虚拟网卡+修改路由表,在第三层网络层代理系统流量。
veth
veth 接口总是成对出现,一对 veth 接口就类似一根网线,从一端进来的数据会从另一端出去。
同时 veth 又是一个虚拟网络接口,因此它和 TUN/TAP 或者其他物理网络接口一样,也都能配置 mac/ip 地址(但是并不是一定得配 mac/ip 地址)。
其主要作用就是连接不同的网络,比如在容器网络中,用于将容器的 namespace 与 root namespace 的网桥 br0 相连。 容器网络中,容器侧的 veth 自身设置了 ip/mac 地址并被重命名为 eth0,作为容器的网络接口使用,而主机侧的 veth 则直接连接在 docker0/br0 上面。
使用 veth 实现容器网络,需要结合下一小节介绍的 bridge,在下一小节将给出容器网络结构图。
bridge
Linux Bridge 是工作在链路层的网络交换机,由 Linux 内核模块 brige
提供,它负责在所有连接到它的接口之间转发链路层数据包。
添加到 Bridge 上的设备被设置为只接受二层数据帧并且转发所有收到的数据包到 Bridge 中。 在 Bridge 中会进行一个类似物理交换机的查MAC端口映射表、转发、更新MAC端口映射表这样的处理逻辑,从而数据包可以被转发到另一个接口/丢弃/广播/发往上层协议栈,由此 Bridge 实现了数据转发的功能。
如果使用 tcpdump 在 Bridge 接口上抓包,可以抓到网桥上所有接口进出的包,因为这些数据包都要通过网桥进行转发。
与物理交换机不同的是,Bridge 本身可以设置 IP 地址,可以认为当使用 brctl addbr br0
新建一个 br0 网桥时,系统自动创建了一个同名的隐藏 br0 网络接口。br0
一旦设置 IP 地址,就意味着这个隐藏的 br0
接口可以作为路由接口设备,参与 IP 层的路由选择(可以使用 route -n
查看最后一列 Iface
)。因此只有当 br0 设置 IP
地址时,Bridge 才有可能将数据包发往上层协议栈。
但被添加到 Bridge 上的网卡是不能配置 IP 地址的,他们工作在数据链路层,对路由系统不可见。
它常被用于在虚拟机、主机上不同的 namepsaces 之间转发数据。
虚拟机场景(桥接模式)
以 qemu-kvm 为例,在虚拟机的桥接模式下,qemu-kvm 会为每个虚拟机创建一个 tun/tap 虚拟网卡并连接到 br0 网桥。 虚拟机内部的网络接口 eth0
是 qemu-kvm 软件模拟的,实际上虚拟机内网络数据的收发都会被 qemu-kvm 转换成对 /dev/net/tun
的读写。
以发送数据为例,整个流程如下:
-
虚拟机发出去的数据包先到达 qemu-kvm 程序。
-
数据被用户层程序 qemu-kvm 写入到 /dev/net/tun,到达 tap 设备。
-
tap 设备把数据传送到 br0 网桥。
-
br0 把数据交给 eth0 发送出去。
整个流程跑完,数据包都不需要经过宿主机的协议栈,效率高。
+------------------------------------------------+--------------------------------以上是关于☀️苏州程序大白解析Linux 中的虚拟网络接口☀️《❤️记得收藏❤️》的主要内容,如果未能解决你的问题,请参考以下文章
☀️苏州程序大白用万字解析Python网络编程与Web编程☀️《❤️记得收藏❤️》
☀️苏州程序大白一文解析Java多线程☀️《❤️记得收藏❤️》
苏州程序大白用2万字解析数据结构和八大排序算法☀️《❤️记得收藏❤️》
苏州程序大白总结Vue零基础到高阶第一节☀️《❤️记得收藏❤️》