【NetDevOps】新一代网工需要了解的那点事儿---网络虚拟化
这篇文章本应该在2018年更详细的记录在《docker by F0rGeEk》中,由那本书正好写到网络章节时出了点状况所以没有继续写下去。。。还是那句老话:出来混早晚都得还
- 1. 网络虚拟化
1. 网络虚拟化
1.1 传统网络虚拟化
随着云计算、大二层、SDN等等这些名词的出现,传统网络架构面临的挑战变得越来越迫切。业内曾有人说“要想实现真正的软件自定义数据中心,网络虚拟化将是最后一公里”从架构上来看确实没毛病。这时候着急的是传统网络厂商,如果不与时俱进则很有可能被淘汰。就好比人家都在玩触屏,你(NOKIA)却还执意玩按键(当然这不是主要原因)。其实传统的网络在前几年数据中心比较火的时候,也催生出很多虚拟化相关的技术。网络虚拟化其实并不陌生,我们常用的Overlays、VLAN、VPN、VRF、HSRP、MPLS这些都是传统网络不断迭代出的虚拟化技术,迫于数据中心、云计算等的需要相继出现了类似STACK、VSS、VDC、VPC、VEB、VEPA、VN-TAG等技术。这些技术有的能在有限的物理网络架构中支持多个逻辑网络,有的能将不同的物理硬件整合到一起,有的则类似计算虚拟化将有限的物理资源虚拟出多个逻辑资源。
最近在看一本书《Kubernetes网络权威指南》,看了部分章节后觉得有必要暂停脚步理一理。首先是感谢作者提供这么一本书,让传统网工有机会接触Linux中的网络虚拟化,感谢这本书中的坑给我机会去了解Linux底层IP路由是如何实现的。其次是要吐槽一下对于Linux底层不了解的读者来说,第一章可能就晕了!亲身经历 有些地方理解起来和传统网络还真有差别,下面我将戴着口罩以一个传统网工的角度来讲解Linux中的网络虚拟化。
1.2 Linux中网络结构
Linux 虚拟网络的基石都是由一个个的虚拟设备构成的。虚拟化技术没出现之前,计算机网络系统都只包含物理的网卡设备,通过网卡适配器,线缆介质,连接外部网络,构成庞大的 Internet(如下图所示)。然而,随着虚拟化技术的出现,网络也随之被虚拟化,相较于单一的物理网络,虚拟网络变得非常复杂,在一个主机系统里面,需要实现诸如交换、路由、隧道、隔离、聚合等多种网络功能。而实现这些功能的基本元素就是虚拟的网络设备,比如 bridge、tap、tun 和 veth/veth-pair。
Physical NIC
+--------------+
| Socket API |
+-------+------+
User Space |
+------------------------------------------+
Kernel Space |
raw ethernet
|
+-------+-------+
| Network Stack |
+-------+-------+
|
+-------+-------+
| eth0 |
+-------+-------+
|
+-------+-------+
| NIC |
+-------+-------+
|
wire
1.3 Linux network namespace
传统网络中我们有一个技术叫VRF,一般我们可以将每一个VRF看作是一台专用的PE设备。它有独立的路由表、地址空间,有属于自己的网络接口、路由协议。在Linux中类似VRF的功能被称作是network namespace,当然Linux还有其他很多的namespace这里就不做过多讲解(因为我不专业)。每个network namespce都有自己的网络设备(如IP、路由表、端口范围、安全策略、/proc/net目录等)。在Linux中维护network namespace主要使用netns这个工具,下面我们逐一展开来讲解。
注:下文为方便书写特将network namespace用NS代替。
1.3.1 netns各参数意义
[root@F0rGeEk ~]# ip netns help # 该命令可获取netns相关帮助文档
Usage: ip netns list # 查看系统中的ns
ip netns add NAME # 创建一个ns
ip netns set NAME NETNSID # 给某个ns分配ID
ip [-all] netns delete [NAME] # 删除一个ns
ip netns identify [PID] # 查看某个进程的ns
ip netns pids NAME # 查找关于这个ns为主的进程
ip [-all] netns exec [NAME] cmd ... # 在某个ns中执行命令
ip netns monitor # 监控对ns的操作
ip netns list-id # 通过list-id显示ns
1.3.2 创建一个ns
我们创建一个名为forgeek的NS,其过程如下;当我们创建一个NS时,系统会自动在/var/run/netns目录中生成一个挂载点。这个挂载点即方便对ns的管理,也是NS在没有进程运行的情况下依然存在。
[root@F0rGeEk ~]# ip netns add forgeek
[root@F0rGeEk netns]# pwd
/var/run/netns
[root@F0rGeEk netns]# ls -l
total 0
-r--r--r--. 1 root root 0 Mar 17 03:23 forgeek
1.3.3 在NS中执行命令
在主机中我们可以在NS中执行一些命令,经过实践发现居然可以在NS中执行bash切换到NS的shell,比较好奇这样在安全上有没有风险。这里为了区分两个shell,我特意将新建的NS中的hostname命名为ns-forgeek:
[root@F0rGeEk ~]# ip netns exec forgeek ip link list
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@F0rGeEk ~]# ip netns exec forgeek bash
[root@ns-forgeek ~]# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@ns-forgeek ~]# exit
exit
[root@F0rGeEk ~]#
1.3.4 激活ns中的环回接口
通过1.2.3实验过程,我们可以看到当我们创建一个ns后,系统默认会给该ns分配一个loopback接口,并且默认情况下这个接口处于DOWN的状态。这里我们将其设置为UP,之所以要在这里花费一个小结做这个,是因为如果该接口处于DOWN状态的话,后期会有很多你想不到的坑在等你!真的是经验之谈
# 方法一:
[root@F0rGeEk ~]# ip netns exec forgeek ip link set dev lo up
[root@F0rGeEk ~]# ip netns exec forgeek ip link list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# 方法二:
[root@F0rGeEk ~]# ip netns exec forgeek bash
[root@ns-forgeek ~]# ip link set dev lo up
[root@ns-forgeek ~]# ip link list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
1.3.5 查询并删除一个NS
[root@F0rGeEk ~]# ip netns list
forgeek
[root@F0rGeEk ~]# ip netns delete forgeek
[root@F0rGeEk ~]# ip netns list
[root@F0rGeEk ~]#
1.4 虚拟网络设备veth-pair
veth从名字上来看是 Virtual Ethernet 的缩写,veth是成对出现的一种虚拟网络设备。一端连接着协议栈,一端连接着彼此,数据从一端出,从另一端进,通常也称作veth-pair(如下图所示)。它的作用很简单,就是要把从一个 network namespace 发出的数据包转发到另一个 namespace中。正因为它的这个特性,常常被用来连接不同的虚拟网络组件,构建大规模的虚拟网络拓扑,比如连接Bridge、OVS、LXC、Docker容器等。很常见的案例就是它被用于Docker网络还有OpenStack Neutron,构建非常复杂的网络形态。
Veth Pair
+--------------+ +--------------+
| Socket API | | Socket API |
+-------+------+ +-------+------+
| |
User Space | |
+------------------------------------------------------------------+
Kernel Space + +
raw ethernet raw ethernet
+ +
+-------+-------+ +-------+-------+
| Network Stack | | Network Stack |
+-------+-------+ +-------+-------+
| |
+-------+-------+ +-------+-------+
| vethX | | vethX |
+-------+-------+ +-------+-------+
| |
+---------------------------+
1.4.1 创建veth-pair
我们通过"ip link"相关命令,创建一对虚拟网卡veth0和veth1。其中给veth0配置IP为12.1.1.1/24,veth1配置IP为12.1.1.2/24,并激活这一对虚拟网卡。"ip link"命令相关参数比较多,可以使用"ip link help"查看这里就不做过多解释。最后通过"ip link list"查看虚拟网卡相关状态,连接图及创建过程如下:
+--------------------------------------------------------------------------------------+
| +-------------------------------------------------------------+ |
| | Network Protocol Stack | |
| +------+------------------------+-----------------------+-----+ |
| ^ ^ ^ |
| | | | |
+--------------------------------------------------------------------------------------+
| | | | |
| v v v |
| +-------+------+ +-----+-----+ +-----+-----+ |
| | Eth0 | | Veth0 | | Veth1 | |
| +-------+------+ +-----+-----+ +------+----+ |
| ^ ^ ^ |
| 10.10.10.137 | 12.1.1.1/24| |12.1.1.2/24 |
| | +------------------------+ |
| | |
+--------------------v-----------------------------------------------------------------+
| Physical Network By:[F0rGeEk] |
+--------------------------------------------------------------------------------------+
- 创建一对veth
[root@d1 ~]# ip link add veth0 type veth peer name veth1
# 查看是否创建成功
[root@d1 ~]# ip link list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:97:0f:70 brd ff:ff:ff:ff:ff:ff
3: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 12:64:bd:95:4f:40 brd ff:ff:ff:ff:ff:ff
4: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 96:10:c9:07:77:d9 brd ff:ff:ff:ff:ff:ff
- 为虚拟网卡配置IP
[root@d1 ~]# ip addr add 12.1.1.1/24 dev veth0
[root@d1 ~]# ip addr add 12.1.1.2/24 dev veth1
# 查看是否配置成功
[root@d1 ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:97:0f:70 brd ff:ff:ff:ff:ff:ff
inet 10.10.10.137/24 brd 10.10.10.255 scope global noprefixroute dynamic ens33
valid_lft 5444486sec preferred_lft 5444486sec
inet6 fe80::d956:a6bf:6a6e:b6a7/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 12:64:bd:95:4f:40 brd ff:ff:ff:ff:ff:ff
inet 12.1.1.2/24 scope global veth1
valid_lft forever preferred_lft forever
4: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 96:10:c9:07:77:d9 brd ff:ff:ff:ff:ff:ff
inet 12.1.1.1/24 scope global veth0
valid_lft forever preferred_lft forever
- 激活这一对虚拟网卡
[root@d1 ~]# ip link set veth0 up
[root@d1 ~]# ip link set veth1 up
# 查看是否激活
[root@d1 ~]# ip link list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:97:0f:70 brd ff:ff:ff:ff:ff:ff
3: veth1@veth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 12:64:bd:95:4f:40 brd ff:ff:ff:ff:ff:ff
4: veth0@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 96:10:c9:07:77:d9 brd ff:ff:ff:ff:ff:ff
1.4.2 veth-pair连通性
接1.4.1步骤这一对虚拟网卡IP地址在同一段且均已激活,这里我们用veth0 ping veth1,查看网络是否可以连通。测试过程如下:
[root@d1 ~]# ping 12.1.1.2 -c 3 -I veth0
PING 12.1.1.2 (12.1.1.2) from 12.1.1.1 veth0: 56(84) bytes of data.
--- 12.1.1.2 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 1999ms
由上面步骤可以看出,实际上这一对虚拟网卡并不能互相通信。可是依据理论来讲,他们是可以互相通信的。这里我们通过抓包来分析一下原因,首先从veth0长pingveth1,然后分别抓veth0和veth1这两个虚拟网卡的包:
- veth0
[root@d1 ~]# tcpdump -nnt -i veth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
ARP, Request who-has 12.1.1.2 tell 12.1.1.1, length 28
ARP, Request who-has 12.1.1.2 tell 12.1.1.1, length 28
ARP, Request who-has 12.1.1.2 tell 12.1.1.1, length 28
- veth1
[root@d1 ~]# tcpdump -nnt -i veth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1, link-type EN10MB (Ethernet), capture size 262144 bytes
ARP, Request who-has 12.1.1.2 tell 12.1.1.1, length 28
ARP, Request who-has 12.1.1.2 tell 12.1.1.1, length 28
ARP, Request who-has 12.1.1.2 tell 12.1.1.1, length 28
通过以上抓包分析:veth0和veth1处于同一网段12.1.1.0/24,由于是第一次通信会通过ARP来确定MAC。可是在两个网卡的抓包情况来看,只有veth0发出的Request包并没有veth1回应的Raply包。有一定传统网络Trouble Shooting功底的您,看到这种现象肯定会想这肯定是防火墙策略阻止了吧您的猜测完全正确,经过查阅相关文档得知:大部分发行版Linux在默认情况下,内核中关于ARP是有一定限制的。所以为了使这一对虚拟网卡在根NS能直接互通,必须要修改默认的策略:
# 修改IP路由默认策略及默认ARP策略
[root@d1 ~]# echo 1 > /proc/sys/net/ipv4/conf/veth1/accept_local
[root@d1 ~]# echo 1 > /proc/sys/net/ipv4/conf/veth0/accept_local
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/veth0/rp_filter
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/veth1/rp_filter
# 接下来再测试连通性
[root@d1 ~]# ping 12.1.1.2 -c 3 -I veth0
PING 12.1.1.2 (12.1.1.2) from 12.1.1.1 veth0: 56(84) bytes of data.
64 bytes from 12.1.1.2: icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from 12.1.1.2: icmp_seq=2 ttl=64 time=0.051 ms
64 bytes from 12.1.1.2: icmp_seq=3 ttl=64 time=0.045 ms
--- 12.1.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2034ms
rtt min/avg/max/mdev = 0.037/0.044/0.051/0.005 ms
注:至于为什么修改上述策略才能通信,过两天还是单独写一篇文章来解释吧。
1.4.3 在NS中的连通性
接下来我们将veth1关联到1.3章节中创建的NS,然后看看和veth0通信的过程。
[root@d1 ~]# ip link set veth1 netns forgeek
[root@d1 ~]# ip netns exec forgeek ifconfig veth1 12.1.1.2/24
[root@d1 ~]# ip netns exec forgeek ip link set dev veth1 up
[root@d1 ~]# ping 12.1.1.2 -c 3 -I veth0
PING 12.1.1.2 (12.1.1.2) from 12.1.1.1 veth0: 56(84) bytes of data.
64 bytes from 12.1.1.2: icmp_seq=1 ttl=64 time=0.115 ms
64 bytes from 12.1.1.2: icmp_seq=2 ttl=64 time=0.066 ms
64 bytes from 12.1.1.2: icmp_seq=3 ttl=64 time=0.072 ms
--- 12.1.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.066/0.084/0.115/0.023 ms
1.4.4 NS之间的连通性
之前我们说veth-pair主要是用来解决不同NS之间通信的,那么接下来我们来创建两个NS:ns1,ns2。然后将veth0
加入到ns1中,veth1加入到ns2中,分别给veth0和veth1配置IP地址如下图所示:
+------------------------------------------------------------------+
| |
| +-----------------+ +-----------------+ |
| | NS1 | | NS2 | |
| | +--+ Veth pair +--+ | |
| | | +--------------------------+ | | |
| | +--+veth0 veth1+--+ | |
| | Name Space |12.1.1.1 12.1.1.2| Name Space | |
| +-----------------+ +-----------------+ |
| |
| Linux Server |
| By:[F0rGeEk] |
+------------------------------------------------------------------|
操作过程如下:
[root@d1 ~]# ip netns add ns1
[root@d1 ~]# ip netns add ns2
[root@d1 ~]# ip link set veth0 netns ns1
[root@d1 ~]# ip link set veth1 netns ns2
# 验证网卡是否加入对应的NS中
[root@d1 ~]# ip netns exec ns1 ip link ls
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
6: veth0@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 02:77:ea:e8:3a:30 brd ff:ff:ff:ff:ff:ff link-netnsid 0
[root@d1 ~]# ip netns exec ns2 ip link ls
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
7: veth1@if8: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether ce:47:33:1f:90:fe brd ff:ff:ff:ff:ff:ff link-netnsid 0
# 激活相应的网卡
[root@d1 ~]# ip netns exec ns1 ip link set dev veth0 up
[root@d1 ~]# ip netns exec ns2 ip link set dev veth1 up
# 为网卡配置IP地址
[root@d1 ~]# ip netns exec ns1 ifconfig veth0 12.1.1.1/24
[root@d1 ~]# ip netns exec ns2 ifconfig veth1 12.1.1.2/24
# 测试连通性
[root@d1 ~]# ip netns exec ns1 ping 12.1.1.2 -c 3 -I veth0
PING 12.1.1.2 (12.1.1.2) from 12.1.1.1 veth0: 56(84) bytes of data.
64 bytes from 12.1.1.2: icmp_seq=1 ttl=64 time=0.051 ms
64 bytes from 12.1.1.2: icmp_seq=2 ttl=64 time=0.057 ms
64 bytes from 12.1.1.2: icmp_seq=3 ttl=64 time=0.052 ms
--- 12.1.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.051/0.053/0.057/0.006 ms
下一小节介绍bridge,未完待续。。。。
1.5 Linux bridge
和veth-pari一样,Linux bridge也是一种虚拟网络设备。这里可以将bridge看作是一个普通的二层交换机,因为它具备二层交换机的所有功能。对于普通的物理设备来说一般只有两端,从一端进来的数据会从另一端出去,比如物理网卡从外面网络中收到的数据会转发到内核协议栈中,而从协议栈过来的数据会转发到外面的物理网络中。而bridge有多个端口,数据可以从多个端口进,也可以从多个端口出去。Bridge的这个特性使得它主要被用于和其他网络设备接入使用,比如虚拟网络设备、物理网卡等。
bridge是建立在从设备上(物理设备、虚拟设备、vlan设备等,即attach一个从设备,类似于现实世界中的交换机和一个用户终端之间连接了一根网线),并且可以为bridge配置一个IP(参考LinuxBridge MAC地址行为),这样该主机就可以通过这个bridge设备与网络中的其他主机进行通信了。另外它的从设备被虚拟化为端口port,它们的IP及MAC都不在可用,且它们被设置为接受任何包,最终由bridge设备来决定数据包的去向:接收到本机、转发、丢弃、广播。
1.5.1 实现原理
Bridge的功能主要在内核里实现。当一个从设备被 attach 到 Bridge 上时,相当于现实世界里交换机的端口被插入了一根连有终端设备的网线。这时在内核程序里,一个用于接受数据的回调函数(netdev_rx_handler_register())被注册。以后每当这个从设备收到数据时都会调用这个函数用来把数据转发到Bridge上。当Bridge接收到此数据时,br_handle_frame()被调用,进行一个和现实世界中的交换机类似的处理过程:判断包的类别(广播/单点),查找内部MAC端口映射表,定位目标端口号,将数据转发到目标端口或丢弃,自动更新内部 MAC 端口映射表以自我学习。
Bridge和一般的二层交换机有一个区别:数据被直接发到 Bridge上,而不是从一个端口接收。这种情况可以看做 Bridge自己有一个MAC可以主动发送报文,或者说Bridge自带了一个隐藏端口和宿主Linux系统自动连接,Linux上的程序可以直接从这个端口向Bridge上的其他端口发数据。所以当一个Bridge拥有一个网络设备时,如 bridge0 加入了 eth0 时,实际上bridge0拥有两个有效MAC地址,一个是bridge0的,一个是eth0的,他们之间可以通讯。这里还有一个有意思的事情是,Bridge可以设置IP地址。通常来说IP地址是三层协议的内容,不应该出现在二层设备Bridge上。但是Linux里Bridge是通用网络设备抽象的一种,只要是网络设备就能够设定IP地址。当一个bridge0拥有IP后,Linux便可以通过路由表或者IP表规则在三层定位bridge0,此时相当于Linux拥有了另外一个隐藏的虚拟网卡和Bridge的隐藏端口相连,这个网卡就是名为bridge0的通用网络设备,IP可以看成是这个网卡的。当有符合此IP的数据到达bridge0时,内核协议栈认为收到了一包目标为本机的数据,此时应用程序可以通过Socket接收到它。一个更好的对比例子是现实世界中的带路由的交换机设备,它也拥有一个隐藏的MAC地址,供设备中的三层协议处理程序和管理程序使用。设备里的三层协议处理程序,对应名为bridge0的通用网络设备的三层协议处理程序,即宿主Linux系统内核协议栈程序。设备里的管理程序,对应bridge0所在宿主Linux 系统里的应用程序。
Bridge的实现在当前有一个限制:当一个设备被attach到Bridge上时,那个设备的IP会变的无效,Linux不再使用那个IP在三层接收数据。举例如下:如果 veth0 本来的IP是 12.1.1.2,此时如果收到一个目标地址是12.1.1.2 的数据,Linux的应用程序能通过Socket操作接收到它。而当veth0被attach到一个bridge0后,尽管veth0的IP还在,但应用程序是无法接收到上述数据的。此时若想收到该数据则应该把IP 12.1.1.2 赋bridge0。
1.5.2 创建bridge
如下图所示,我们创建一对veth-pari:veth0、veth1;然后创建一个Bridge:br0,其中veth0配置IP地址为12.1.1.1/24,veth1配置IP地址为12.1.1.2/24。然后我们将veth0加入到br0中,此时我们在veth1上ping12.1.1.1测试网络是否连通。为验证1.5.1中介绍原理,我们将veth0的IP地址移至br0,此时再通过veth1去ping12.1.1.1测试网络是否连通。实验过程如下:
+----------------------------------------------------------+
| By:[f0rGeEk] |
| +--------------------------------------------------+ |
| | | |
| | Network Protocal Stack | |
| | | |
| +---+--------------+--------------+------------+---+ |
| ^ ^ | ^ |
| | | | | |
+----------------------------------------------------------+
| | | | | |
| | | | | |
| v v v v |
| +--+---+ +----+---+ +--+---+ +--+---+ |
| | | | | | | | | |
| | eth0 | | br0 |<----->+ veth0| | veth1| |
| | | |12.1.1.1/24 | | |12.1.1.2/24
| +---+--+ +--------+ +--+---+ +---+--+ |
| ^ ^ ^ |
| | | | |
| | +-------------+ |
| | |
+----------------------------------------------------------+
|
v
Physical Network
- 创建veth-pair、bridge
[root@d1 ~]# ip link add veth0 type veth peer name veth1
[root@d1 ~]# ip link set dev veth0 up
[root@d1 ~]# ip link set dev veth1 up
[root@d1 ~]# ip addr add 12.1.1.1/24 dev veth0
[root@d1 ~]# ip addr add 12.1.1.2/24 dev veth1
# 创建并激活一个bridge
[root@d1 ~]# ip link add name br0 type bridge
[root@d1 ~]# ip link set br0 up
[root@d1 ~]# ip link list br0
9: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether a2:47:b8:b7:21:9d brd ff:ff:ff:ff:ff:ff
# 将veth0加入bridge中
[root@d1 ~]# ip link set dev veth0 master br0
[root@d1 ~]# ip link list br0
9: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 22:d0:1d:fd:5d:9b brd ff:ff:ff:ff:ff:ff
# 查看桥接信息
[root@d1 ~]# bridge link
8: veth0 state UP @veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2
# 首先修改各虚拟网络设备的access_local和rp_filter
[root@d1 ~]# echo 1 > /proc/sys/net/ipv4/conf/br0/accept_local
[root@d1 ~]# echo 1 > /proc/sys/net/ipv4/conf/veth1/accept_local
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/br0/rp_filter
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/veth1/rp_filter
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
[root@d1 ~]# echo 0 > /proc/sys/net/ipv4/conf/default/rp_filter
仔细观察上述过程,我们发现一个有趣的现象,当veth0加入bridge br0后,br0的MAC地址则变成了veth0的MAC地址。下面我们在veth1上ping12.1.1.1测试连通性:
- 测试
[root@d1 ~]# ping -c 3 -I veth1 12.1.1.1
ping: Warning: source address might be selected on device other than veth1.
PING 12.1.1.1 (12.1.1.1) from 10.10.10.137 veth1: 56(84) bytes of data.
--- 12.1.1.1 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2009ms
- 为bridge配置IP地址
[root@d1 ~]# ip addr del 12.1.1.1/24 dev veth0
[root@d1 ~]# ip addr add 12.1.1.1/24 dev br0
- 测试
[root@d1 ~]# ping -c 2 -I veth1 12.1.1.1
PING 12.1.1.1 (12.1.1.1) from 12.1.1.2 veth1: 56(84) bytes of data.
64 bytes from 12.1.1.1: icmp_seq=1 ttl=64 time=0.049 ms
64 bytes from 12.1.1.1: icmp_seq=2 ttl=64 time=0.072 ms
1.5.3 连接两个NS
如下图所示,我们创建两个NS:ns1、ns2,同时创建两对veth-pari:veth0>br-veth0;veth>br-veth1。创建一个bridge:br0,并将veth0和veth1加入至br0然后为br-veth0和br-veth1分别配置IP地址。最后在br-veth0接口上ping12.1.1.2测试网络是否连通。
+-----------------------------------------------------------------------------+
| By:[F0rGeEk] |
| Linux Bridge connetc NS |
| +--------------------+ +-------------------+ |
| | NameSpace NS1| | NameSpace NS2| |
| | | | | |
| | br-veth1 | | br-veth0 | |
| | +---+ | | +---+ | |
| | | | | | | | | |
| +-------+---+--------+ +--------+---+------+ |
| |12.1.1.2/24 12.1.1.1/24| |
| | | |
| | +---------------+ | |
| | veth-pari | | veth-pari | |
| | +--+ +--+ | |
| +------------------+ | | +-------------------+ |
| veth1+--+ +--+ veth0 |
| | Bridge 0 | |
| +---------------+ |
+-----------------------------------------------------------------------------+
- 创建ns1、ns2、br0 两对veth-pair
[root@d1 ~]# ip netns add ns1
[root@d1 ~]# ip netns add ns2
[root@d1 ~]# ip link add veth0 type veth peer name br-veth0
[root@d1 ~]# ip link add veth1 type veth peer name br-veth1
[root@d1 ~]# ip link add name br0 type bridge
[root@d1 ~]# ip link set dev veth0 up
[root@d1 ~]# ip link set dev veth1 up
[root@d1 ~]# ip link set dev br-veth0 up
[root@d1 ~]# ip link set dev br-veth1 up
[root@d1 ~]# ip link set dev br0 up
- 将veth-pair分别加入NS和bridge中,并激活NS中的所有接口
[root@d1 ~]# ip link set br-veth0 netns ns2
[root@d1 ~]# ip link set br-veth1 netns ns1
[root@d1 ~]# ip link set dev veth0 master br0
[root@d1 ~]# ip link set dev veth1 master br0
# 激活NS中的接口
[root@d1 ~]# ip netns exec ns1 ifconfig br-veth1 up
[root@d1 ~]# ip netns exec ns2 ifconfig br-veth0 up
- 配置IP地址
[root@d1 ~]# ip netns exec ns1 ifconfig br-veth1 12.1.1.1/24
[root@d1 ~]# ip netns exec ns2 ifconfig br-veth0 12.1.1.2/24
- 检查上述配置
# 查看NS中网卡IP配置
[root@d1 ~]# ip netns exec ns1 ip addr show br-veth1
5: br-veth1@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 1a:36:ef:1e:8b:98 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 12.1.1.1/24 brd 12.1.1.255 scope global br-veth1
valid_lft forever preferred_lft forever
inet6 fe80::1836:efff:fe1e:8b98/64 scope link
valid_lft forever preferred_lft forever
[root@d1 ~]# ip netns exec ns2 ip addr show br-veth0
3: br-veth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether d2:45:60:46:ab:77 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 12.1.1.2/24 brd 12.1.1.255 scope global br-veth0
valid_lft forever preferred_lft forever
inet6 fe80::d045:60ff:fe46:ab77/64 scope link
valid_lft forever preferred_lft forever
# 检查bridge配置
[root@d1 ~]# bridge link
4: veth0 state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2
6: veth1 state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2
- 测试两个NS之间的连通性
# ipv4连通测试
[root@d1 ~]# ip netns exec ns2 ping 12.1.1.1 -c3 -I br-veth0
PING 12.1.1.1 (12.1.1.1) from 12.1.1.2 br-veth0: 56(84) bytes of data.
64 bytes from 12.1.1.1: icmp_seq=1 ttl=64 time=0.058 ms
64 bytes from 12.1.1.1: icmp_seq=2 ttl=64 time=0.062 ms
64 bytes from 12.1.1.1: icmp_seq=3 ttl=64 time=0.064 ms
--- 12.1.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.058/0.061/0.064/0.006 ms
# ipv6连通测试
[root@d1 ~]# ip netns exec ns2 ping6 fe80::1836:efff:fe1e:8b98 -c 3 -I br-veth0
PING fe80::1836:efff:fe1e:8b98(fe80::1836:efff:fe1e:8b98) from fe80::d045:60ff:fe46:ab77%br-veth0 br-veth0: 56 data bytes
64 bytes from fe80::1836:efff:fe1e:8b98%br-veth0: icmp_seq=1 ttl=64 time=0.057 ms
64 bytes from fe80::1836:efff:fe1e:8b98%br-veth0: icmp_seq=2 ttl=64 time=0.067 ms
64 bytes from fe80::1836:efff:fe1e:8b98%br-veth0: icmp_seq=3 ttl=64 time=0.069 ms
--- fe80::1836:efff:fe1e:8b98 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.057/0.064/0.069/0.008 ms
1.6 tun/tap
待更新。。。。。。。
1.7 iptables
待更新。。。。。。。
1.8 ipip
待更新。。。。。。。
1.9 VXLAN
待更新。。。。。。。
1.10 MacVLAN
待更新。。。。。。。
