Linux veth pair 与 bridge 实验

-
-
2024-05-13

 

Linux veth pair 

veth pair是成对出现的一种虚拟网络设备接口,一端连着网络协议栈,一端彼此相连。如下图所示:

由于它的这个特性,常常被用于构建虚拟网络拓扑。例如连接两个不同的网络命名空间(netns),连接docker容器,连接网桥(Bridge)等,其中一个很常见的案例就是OpenStack Neutron底层用它来构建非常复杂的网络拓扑。

如何使用?

创建一对veth

ip link add <veth name> type veth peer name <peer name>

实验

我们改造上一节完成的netns实验,使用veth pair将两个的隔离netns连接起来。如下图所示:

我们首先创建一对veth设备,将veth设备分别移动到两个netns中并启动。

# 创建一对veth
ip link add veth0 type veth peer name veth1
# 将veth移动到netns中
ip link set veth0 netns ns0
ip link set veth1 netns ns1
# 启动
ip netns exec ns0 ip link set veth0 up
ip netns exec ns1 ip link set veth1 up

接下来我们测试一下。

使用ip netns exec ns0 ping 10.0.0.2在命名空间ns0中测试与tap1的网络连通性。

PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
From 10.0.0.1 icmp_seq=1 Destination Host Unreachable
From 10.0.0.1 icmp_seq=2 Destination Host Unreachable
From 10.0.0.1 icmp_seq=3 Destination Host Unreachable
^C
--- 10.0.0.2 ping statistics ---
5 packets transmitted, 0 received, +3 errors, 100% packet loss, time 77ms
pipe 4

使用ip netns exec ns1 ping 10.0.0.1在命名空间ns1中测试与tap0的网络连通性。

PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
From 10.0.0.2 icmp_seq=1 Destination Host Unreachable
From 10.0.0.2 icmp_seq=2 Destination Host Unreachable
From 10.0.0.2 icmp_seq=3 Destination Host Unreachable
^C
--- 10.0.0.1 ping statistics ---
4 packets transmitted, 0 received, +3 errors, 100% packet loss, time 108ms
pipe 4

什么情况?为什么网络还是不通呢?答案就是路由配置有问题。

使用ip netns exec ns0 route -n查看ns0的路由表。

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 tap0

使用ip netns exec ns1 route -n查看ns1的路由表。

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 tap1

原来访问10.0.0.0/24的流量都从tap设备发出去了,又因为tap设备没有和其他设备相连,发出去的数据报文不会被处理,因此还是访问不到目标IP,我们来修改一下路由,让访问10.0.0.0/24的流量从veth设备发出。

#修改路由出口为veth
ip netns exec ns0 ip route change 10.0.0.0/24 via 0.0.0.0 dev veth0
ip netns exec ns1 ip route change 10.0.0.0/24 via 0.0.0.0 dev veth1

我们再来看一下路由

使用ip netns exec ns0 route -n查看ns0的路由表。

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 veth0

使用ip netns exec ns1 route -n查看ns1的路由表。

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 veth1

最后我们再来测试一下。

使用ip netns exec ns0 ping 10.0.0.2在命名空间ns0中测试与tap1的网络连通性。

PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.031 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.035 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.037 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.043 ms
^C
--- 10.0.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 103ms
rtt min/avg/max/mdev = 0.031/0.036/0.043/0.007 ms

使用ip netns exec ns1 ping 10.0.0.1在命名空间ns1中测试与tap0的网络连通性。

PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.027 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.051 ms
64 bytes from 10.0.0.1: icmp_seq=4 ttl=64 time=0.042 ms
^C
--- 10.0.0.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 66ms
rtt min/avg/max/mdev = 0.027/0.041/0.051/0.012 ms

可以看到我们使用veth pair将两个隔离的netns成功的连接到了一起。

但是这样的网络拓扑存在一个弊端,随着网络设备的增多,网络连线的复杂度将成倍增长。 如果连接三个netns时,网络连线就成了下图的样子

而如果连接四个netns时,网络连线就成了下图的样子

如果有五台设备。。。

有没有什么技术可以解决这个问题呢?答案是有的,Linux Bridge(网桥)。我们将使用网桥来将多个隔离的netns连接起来,这样网络连线就非常清爽了。

 

Linux Bridge 

Linux Bridge(网桥)是用纯软件实现的虚拟交换机,有着和物理交换机相同的功能,例如二层交换,MAC地址学习等。因此我们可以把tun/tap,veth pair等设备绑定到网桥上,就像是把设备连接到物理交换机上一样。此外它和veth pair、tun/tap一样,也是一种虚拟网络设备,具有虚拟设备的所有特性,例如配置IP,MAC地址等。

Linux Bridge通常是搭配KVM、docker等虚拟化技术一起使用的,用于构建虚拟网络,因为此教程不涉及虚拟化技术,我们就使用前面学习过的netns来模拟虚拟设备。

如何使用Linux Bridge?

操作网桥有多种方式,在这里我们介绍一下通过bridge-utils来操作,由于它不是Linux系统自带的工具,因此需要我们手动来安装它。

# centos
yum install -y bridge-utils
# ubuntu
apt-get install -y bridge-utils

使用brctl help查看使用帮助

never heard of command [help]
Usage: brctl [commands]
commands:
 addbr      <bridge>  add bridge
 delbr      <bridge>  delete bridge
 addif      <bridge> <device> add interface to bridge
 delif      <bridge> <device> delete interface from bridge
 hairpin    <bridge> <port> {on|off} turn hairpin on/off
 setageing  <bridge> <time>  set ageing time
 setbridgeprio <bridge> <prio>  set bridge priority
 setfd      <bridge> <time>  set bridge forward delay
 sethello   <bridge> <time>  set hello time
 setmaxage  <bridge> <time>  set max message age
 setpathcost <bridge> <port> <cost> set path cost
 setportprio <bridge> <port> <prio> set port priority
 show       [ <bridge> ]  show a list of bridges
 showmacs   <bridge>  show a list of mac addrs
 showstp    <bridge>  show bridge stp info
 stp        <bridge> {on|off} turn stp on/off

常用命令如

新建一个网桥:

brctl addbr <bridge>

添加一个设备(例如eth0)到网桥:

brctl addif <bridge> eth0

显示当前存在的网桥及其所连接的网络端口:

brctl show

启动网桥:

ip link set <bridge> up

删除网桥,需要先关闭它:

ip link set <bridge> down
brctl delbr <bridge>

或者使用ip link del 命令直接删除网桥

ip link del <bridge>

增加Linux Bridge时会自动增加一个同名虚拟网卡在宿主机器上,因此我们可以通过ip link命令操作这个虚拟网卡,实际上也就是操作网桥,并且只有当这个虚拟网卡状态处于up的时候,网桥才会转发数据。

实验

第一部分我们使用veth pair将两个隔离的netns连接在了一起,在现实世界里等同于用一根网线把两台电脑连接在了一起,但是在现实世界里往往很少会有人这样使用。因为一台设备不仅仅只需要和另一台设备通信,它需要和很多很多的网络设备进行通信,如果还使用这样的方式,需要十分复杂的网络接线,并且现实世界中的普通网络设备也没有那么多网络接口。

那么,想要让某一台设备和很多网络设备都可以通信需要如何去做呢?在我们的日常生活中,除了手机和电脑,最常见的网络设备就是路由器了,我们的手机连上WI-FI,电脑插到路由器上,等待从路由器的DHCP服务器上获取到IP,他们就可以相互通信了,这便是路由器的二层交换功能在工作。Linux Bridge最主要的功能就是二层交换,是对现实世界二层交换机的模拟,我们稍微改动一下网络拓扑,如下图:

我们建立了一个网桥,三个netns,三对veth pair,分别一端在netns中,另一端连接在网桥上,为了简化拓扑,我去除了netns中的tap设备,将IP直接配置在veth上。

veth设备不仅仅可以可以充当“网线”,同时它也可以当作虚拟网卡来使用。
 

# 添加网桥
brctl addbr br0
# 启动网桥
ip link set br0 up

# 新增三个netns
ip netns add ns0
ip netns add ns1
ip netns add ns2

# 新增两对veth
ip link add veth0-ns type veth peer name veth0-br
ip link add veth1-ns type veth peer name veth1-br
ip link add veth2-ns type veth peer name veth2-br

# 将veth的一端移动到netns中
ip link set veth0-ns netns ns0
ip link set veth1-ns netns ns1
ip link set veth2-ns netns ns2

# 将netns中的本地环回和veth启动并配置IP
ip netns exec ns0 ip link set lo up
ip netns exec ns0 ip link set veth0-ns up
ip netns exec ns0 ip addr add 10.0.0.1/24 dev veth0-ns

ip netns exec ns1 ip link set lo up
ip netns exec ns1 ip link set veth1-ns up
ip netns exec ns1 ip addr add 10.0.0.2/24 dev veth1-ns

ip netns exec ns2 ip link set lo up
ip netns exec ns2 ip link set veth2-ns up
ip netns exec ns2 ip addr add 10.0.0.3/24 dev veth2-ns

# 将veth的另一端启动并挂载到网桥上
ip link set veth0-br up
ip link set veth1-br up
ip link set veth2-br up
brctl addif br0 veth0-br
brctl addif br0 veth1-br
brctl addif br0 veth2-br

测试网络连通性

使用ip netns exec ns0 ping 10.0.0.2在命名空间ns0中测试与ns1的10.0.0.2的网络连通性

PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.032 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.058 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.052 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.044 ms
^C
--- 10.0.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 54ms
rtt min/avg/max/mdev = 0.032/0.046/0.058/0.011 ms

使用ip netns exec ns0 ping 10.0.0.3在命名空间ns0中测试与ns2的10.0.0.3的网络连通性

PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data.
64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=0.054 ms
64 bytes from 10.0.0.3: icmp_seq=2 ttl=64 time=0.045 ms
64 bytes from 10.0.0.3: icmp_seq=3 ttl=64 time=0.058 ms
64 bytes from 10.0.0.3: icmp_seq=4 ttl=64 time=0.064 ms
^C
--- 10.0.0.3 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 81ms
rtt min/avg/max/mdev = 0.045/0.055/0.064/0.008 ms

使用ip netns exec ns1 ping 10.0.0.1在命名空间ns1中测试与ns0的10.0.0.1的网络连通性

PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.031 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.046 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.038 ms
64 bytes from 10.0.0.1: icmp_seq=4 ttl=64 time=0.041 ms
^C
--- 10.0.0.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 81ms
rtt min/avg/max/mdev = 0.031/0.039/0.046/0.005 ms

使用ip netns exec ns1 ping 10.0.0.3在命名空间ns1中测试与ns2的10.0.0.3的网络连通性

PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data.
64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=0.060 ms
64 bytes from 10.0.0.3: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 10.0.0.3: icmp_seq=3 ttl=64 time=0.044 ms
64 bytes from 10.0.0.3: icmp_seq=4 ttl=64 time=0.065 ms
^C
--- 10.0.0.3 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 65ms
rtt min/avg/max/mdev = 0.044/0.057/0.065/0.007 ms

使用ip netns exec ns2 ping 10.0.0.1在命名空间ns2中测试与ns0的10.0.0.1的网络连通性

PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.032 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.056 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.043 ms
64 bytes from 10.0.0.1: icmp_seq=4 ttl=64 time=0.060 ms
^C
--- 10.0.0.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 69ms
rtt min/avg/max/mdev = 0.032/0.047/0.060/0.013 ms

使用ip netns exec ns2 ping 10.0.0.2在命名空间ns2中测试与ns1的10.0.0.2的网络连通性

PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.030 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.055 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.044 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.042 ms
^C
--- 10.0.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 114ms
rtt min/avg/max/mdev = 0.030/0.042/0.055/0.011 ms

可以看到我们通过网桥的方式把三个隔离的netns连接在了一起,通过这种方式,我们还可以很方便的添加第四个netns,第五个netns...在这里我们就不展开了,感兴趣的同学可以尝试一下。

转自:整点bug

微信公众号:整点bug

Linux Bridge 详解 - 知乎 (zhihu.com)

“您的支持是我持续分享的动力”

微信收款码
微信
支付宝收款码
支付宝

目录